<!DOCTYPE html><html lang="zh-CN" data-theme="light"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"><title>Netty入门-第三话 | 石银的小酒屋</title><meta name="keywords" content="Netty"><meta name="author" content="Alix"><meta name="copyright" content="Alix"><meta name="format-detection" content="telephone=no"><meta name="theme-color" content="#ffffff"><meta name="description" content="对前面两话一些迷惑的点进行细说，讲解handler调用机制，TCP粘包，以及用netty写一个十分简单的RPC。">
<meta property="og:type" content="article">
<meta property="og:title" content="Netty入门-第三话">
<meta property="og:url" content="http://example.com/2022/04/21/netty/Netty%E5%85%A5%E9%97%A8-%E7%AC%AC%E4%B8%89%E8%AF%9D/index.html">
<meta property="og:site_name" content="石银的小酒屋">
<meta property="og:description" content="对前面两话一些迷惑的点进行细说，讲解handler调用机制，TCP粘包，以及用netty写一个十分简单的RPC。">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://npm.elemecdn.com/lql_static@latest/logo/netty_logo.jpg">
<meta property="article:published_time" content="2022-04-21T09:38:58.000Z">
<meta property="article:modified_time" content="2023-04-20T10:49:30.079Z">
<meta property="article:author" content="Alix">
<meta property="article:tag" content="Netty">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://npm.elemecdn.com/lql_static@latest/logo/netty_logo.jpg"><link rel="shortcut icon" href="/assetse/%E5%A4%B4%E5%83%8F.jpg"><link rel="canonical" href="http://example.com/2022/04/21/netty/Netty%E5%85%A5%E9%97%A8-%E7%AC%AC%E4%B8%89%E8%AF%9D/"><link rel="preconnect" href="//cdn.jsdelivr.net"/><link rel="preconnect" href="//busuanzi.ibruce.info"/><link rel="stylesheet" href="/css/index.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free/css/all.min.css" media="print" onload="this.media='all'"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox.min.css" media="print" onload="this.media='all'"><script>const GLOBAL_CONFIG = { 
  root: '/',
  algolia: undefined,
  localSearch: undefined,
  translate: undefined,
  noticeOutdate: undefined,
  highlight: {"plugin":"highlighjs","highlightCopy":true,"highlightLang":true,"highlightHeightLimit":false},
  copy: {
    success: '复制成功',
    error: '复制错误',
    noSupport: '浏览器不支持'
  },
  relativeDate: {
    homepage: false,
    post: false
  },
  runtime: '',
  date_suffix: {
    just: '刚刚',
    min: '分钟前',
    hour: '小时前',
    day: '天前',
    month: '个月前'
  },
  copyright: undefined,
  lightbox: 'fancybox',
  Snackbar: undefined,
  source: {
    justifiedGallery: {
      js: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery/dist/fjGallery.min.js',
      css: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery/dist/fjGallery.min.css'
    }
  },
  isPhotoFigcaption: false,
  islazyload: false,
  isAnchor: false
}</script><script id="config-diff">var GLOBAL_CONFIG_SITE = {
  title: 'Netty入门-第三话',
  isPost: true,
  isHome: false,
  isHighlightShrink: false,
  isToc: true,
  postUpdate: '2023-04-20 18:49:30'
}</script><noscript><style type="text/css">
  #nav {
    opacity: 1
  }
  .justified-gallery img {
    opacity: 1
  }

  #recent-posts time,
  #post-meta time {
    display: inline !important
  }
</style></noscript><script>(win=>{
    win.saveToLocal = {
      set: function setWithExpiry(key, value, ttl) {
        if (ttl === 0) return
        const now = new Date()
        const expiryDay = ttl * 86400000
        const item = {
          value: value,
          expiry: now.getTime() + expiryDay,
        }
        localStorage.setItem(key, JSON.stringify(item))
      },

      get: function getWithExpiry(key) {
        const itemStr = localStorage.getItem(key)

        if (!itemStr) {
          return undefined
        }
        const item = JSON.parse(itemStr)
        const now = new Date()

        if (now.getTime() > item.expiry) {
          localStorage.removeItem(key)
          return undefined
        }
        return item.value
      }
    }
  
    win.getScript = url => new Promise((resolve, reject) => {
      const script = document.createElement('script')
      script.src = url
      script.async = true
      script.onerror = reject
      script.onload = script.onreadystatechange = function() {
        const loadState = this.readyState
        if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
        script.onload = script.onreadystatechange = null
        resolve()
      }
      document.head.appendChild(script)
    })
  
      win.activateDarkMode = function () {
        document.documentElement.setAttribute('data-theme', 'dark')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#0d0d0d')
        }
      }
      win.activateLightMode = function () {
        document.documentElement.setAttribute('data-theme', 'light')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#ffffff')
        }
      }
      const t = saveToLocal.get('theme')
    
          if (t === 'dark') activateDarkMode()
          else if (t === 'light') activateLightMode()
        
      const asideStatus = saveToLocal.get('aside-status')
      if (asideStatus !== undefined) {
        if (asideStatus === 'hide') {
          document.documentElement.classList.add('hide-aside')
        } else {
          document.documentElement.classList.remove('hide-aside')
        }
      }
    
    const detectApple = () => {
      if(/iPad|iPhone|iPod|Macintosh/.test(navigator.userAgent)){
        document.documentElement.classList.add('apple')
      }
    }
    detectApple()
    })(window)</script><link rel="stylesheet" href="/self/Kimbiedark.css"><meta name="generator" content="Hexo 6.2.0"><link rel="alternate" href="/atom.xml" title="石银的小酒屋" type="application/atom+xml">
</head><body><div id="loading-box"><div class="loading-left-bg"></div><div class="loading-right-bg"></div><div class="spinner-box"><div class="configure-border-1"><div class="configure-core"></div></div><div class="configure-border-2"><div class="configure-core"></div></div><div class="loading-word">加载中...</div></div></div><div id="web_bg"></div><div id="sidebar"><div id="menu-mask"></div><div id="sidebar-menus"><div class="avatar-img is-center"><img src="/assetse/%E5%A4%B4%E5%83%8F.jpg" onerror="onerror=null;src='/img/friend_404.gif'" alt="avatar"/></div><div class="sidebar-site-data site-data is-center"><a href="/archives/"><div class="headline">文章</div><div class="length-num">23</div></a><a href="/tags/"><div class="headline">标签</div><div class="length-num">9</div></a><a href="/categories/"><div class="headline">分类</div><div class="length-num">7</div></a></div><hr/><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw fas fa-home"></i><span> 主页</span></a></div><div class="menus_item"><a class="site-page group" href="javascript:void(0);"><i class="fa-fw fa fa-graduation-cap"></i><span> 博文</span><i class="fas fa-chevron-down"></i></a><ul class="menus_item_child"><li><a class="site-page child" href="/categories/"><i class="fa-fw fa fa-archive"></i><span> 分类</span></a></li><li><a class="site-page child" href="/tags/"><i class="fa-fw fa fa-tags"></i><span> 标签</span></a></li><li><a class="site-page child" href="/archives/"><i class="fa-fw fa fa-folder-open"></i><span> 归档</span></a></li></ul></div><div class="menus_item"><a class="site-page group" href="javascript:void(0);"><i class="fa-fw fas fa-list"></i><span> 生活</span><i class="fas fa-chevron-down"></i></a><ul class="menus_item_child"><li><a class="site-page child" href="/photos/"><i class="fa-fw fa fa-camera-retro"></i><span> 相册</span></a></li><li><a class="site-page child" href="/playlist/"><i class="fa-fw fa fa-music"></i><span> 音乐</span></a></li><li><a class="site-page child" href="/bangumis/"><i class="fa-fw fas fa-video"></i><span> 番剧</span></a></li></ul></div><div class="menus_item"><a class="site-page" href="/link/"><i class="fa-fw fa fa-link"></i><span> 友链</span></a></div><div class="menus_item"><a class="site-page" href="/about/"><i class="fa-fw fas fa-heart"></i><span> 关于笔者</span></a></div></div></div></div><div class="post" id="body-wrap"><header class="post-bg" id="page-header" style="background-image: url('https://npm.elemecdn.com/lql_static@latest/logo/netty_logo.jpg')"><nav id="nav"><span id="blog_name"><a id="site-name" href="/">石银的小酒屋</a></span><div id="menus"><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw fas fa-home"></i><span> 主页</span></a></div><div class="menus_item"><a class="site-page group" href="javascript:void(0);"><i class="fa-fw fa fa-graduation-cap"></i><span> 博文</span><i class="fas fa-chevron-down"></i></a><ul class="menus_item_child"><li><a class="site-page child" href="/categories/"><i class="fa-fw fa fa-archive"></i><span> 分类</span></a></li><li><a class="site-page child" href="/tags/"><i class="fa-fw fa fa-tags"></i><span> 标签</span></a></li><li><a class="site-page child" href="/archives/"><i class="fa-fw fa fa-folder-open"></i><span> 归档</span></a></li></ul></div><div class="menus_item"><a class="site-page group" href="javascript:void(0);"><i class="fa-fw fas fa-list"></i><span> 生活</span><i class="fas fa-chevron-down"></i></a><ul class="menus_item_child"><li><a class="site-page child" href="/photos/"><i class="fa-fw fa fa-camera-retro"></i><span> 相册</span></a></li><li><a class="site-page child" href="/playlist/"><i class="fa-fw fa fa-music"></i><span> 音乐</span></a></li><li><a class="site-page child" href="/bangumis/"><i class="fa-fw fas fa-video"></i><span> 番剧</span></a></li></ul></div><div class="menus_item"><a class="site-page" href="/link/"><i class="fa-fw fa fa-link"></i><span> 友链</span></a></div><div class="menus_item"><a class="site-page" href="/about/"><i class="fa-fw fas fa-heart"></i><span> 关于笔者</span></a></div></div><div id="toggle-menu"><a class="site-page"><i class="fas fa-bars fa-fw"></i></a></div></div></nav><div id="post-info"><h1 class="post-title">Netty入门-第三话</h1><div id="post-meta"><div class="meta-firstline"><span class="post-meta-date"><i class="far fa-calendar-alt fa-fw post-meta-icon"></i><span class="post-meta-label">发表于</span><time class="post-meta-date-created" datetime="2022-04-21T09:38:58.000Z" title="发表于 2022-04-21 17:38:58">2022-04-21</time><span class="post-meta-separator">|</span><i class="fas fa-history fa-fw post-meta-icon"></i><span class="post-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2023-04-20T10:49:30.079Z" title="更新于 2023-04-20 18:49:30">2023-04-20</time></span><span class="post-meta-categories"><span class="post-meta-separator">|</span><i class="fas fa-inbox fa-fw post-meta-icon"></i><a class="post-meta-categories" href="/categories/Netty/">Netty</a><i class="fas fa-angle-right post-meta-separator"></i><i class="fas fa-inbox fa-fw post-meta-icon"></i><a class="post-meta-categories" href="/categories/Netty/%E5%85%A5%E9%97%A8/">入门</a></span></div><div class="meta-secondline"><span class="post-meta-separator">|</span><span class="post-meta-wordcount"><i class="far fa-file-word fa-fw post-meta-icon"></i><span class="post-meta-label">字数总计:</span><span class="word-count">12.7k</span><span class="post-meta-separator">|</span><i class="far fa-clock fa-fw post-meta-icon"></i><span class="post-meta-label">阅读时长:</span><span>54分钟</span></span><span class="post-meta-separator">|</span><span class="post-meta-pv-cv" id="" data-flag-title="Netty入门-第三话"><i class="far fa-eye fa-fw post-meta-icon"></i><span class="post-meta-label">阅读量:</span><span id="busuanzi_value_page_pv"><i class="fa-solid fa-spinner fa-spin"></i></span></span></div></div></div></header><main class="layout" id="content-inner"><div id="post"><article class="post-content" id="article-container"><blockquote>
<p>尚硅谷的源码部分暂时不再记录笔记，因为我觉得源码这东西，韩老师讲的不太好，弹幕和评论也有说。源码这东西，先把入门的消化一下，然后通过书或者博客来看源码会比较好，你只有先会用，看源码才会有感觉。</p>
</blockquote>
<h1 id="Google-Protobuf"><a href="#Google-Protobuf" class="headerlink" title="Google Protobuf"></a>Google Protobuf</h1><h2 id="编码和解码的基本介绍"><a href="#编码和解码的基本介绍" class="headerlink" title="编码和解码的基本介绍"></a>编码和解码的基本介绍</h2><ol>
<li>编写网络应用程序时，因为数据在网络中传输的都是二进制字节码数据，在发送数据时就需要编码，接收数据时就需要解码[示意图]</li>
<li><code>codec</code>（编解码器）的组成部分有两个：<code>decoder</code>（解码器）和 <code>encoder</code>（编码器）。<code>encoder</code> 负责把业务数据转换成字节码数据，<code>decoder</code> 负责把字节码数据转换成业务数据</li>
</ol>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0001.png"/>

<h2 id="Netty-本身的编码解码的机制和问题分析"><a href="#Netty-本身的编码解码的机制和问题分析" class="headerlink" title="Netty 本身的编码解码的机制和问题分析"></a>Netty 本身的编码解码的机制和问题分析</h2><ol>
<li><code>Netty</code> 自身提供了一些 <code>codec</code>(编解码器)</li>
<li><code>Netty</code> 提供的编码器 <ul>
<li><code>StringEncoder</code>：对字符串数据进行编码。</li>
<li><code>ObjectEncoder</code>：对Java对象进行编码。</li>
</ul>
</li>
<li><code>Netty</code> 提供的解码器 <ul>
<li><code>StringDecoder</code>,对字符串数据进行解码 </li>
<li><code>ObjectDecoder</code>，对 Java 对象进行解码</li>
</ul>
</li>
<li><code>Netty</code> 本身自带的 <code>ObjectDecoder</code> 和 <code>ObjectEncoder</code> 可以用来实现 <code>POJO</code> 对象或各种业务对象的编码和解码，底层使用的仍是Java序列化技术,而Java序列化技术本身效率就不高，存在如下问题<ul>
<li>无法跨语言</li>
<li>序列化后的体积太大，是二进制编码的5倍多。</li>
<li>序列化性能太低</li>
</ul>
</li>
<li>引出新的解决方案[<code>Google</code> 的 <code>Protobuf</code>]</li>
</ol>
<h2 id="Protobuf"><a href="#Protobuf" class="headerlink" title="Protobuf"></a>Protobuf</h2><ol>
<li><code>Protobuf</code> 基本介绍和使用示意图</li>
<li><code>Protobuf</code> 是 <code>Google</code> 发布的开源项目，全称 <code>Google Protocol Buffers</code>，是一种轻便高效的结构化数据存储格式，可以用于结构化数据串行化，或者说序列化。它很适合做数据存储或 <code>RPC</code> [远程过程调用 <code>remote procedure call</code> ]数据交换格式。目前很多公司 从<code>http + json 转向tcp + protobuf</code>，效率会更高。</li>
<li>参考文档：<a target="_blank" rel="noopener" href="https://developers.google.com/protocol-buffers/docs/proto">https://developers.google.com/protocol-buffers/docs/proto</a> 语言指南</li>
<li><code>Protobuf</code> 是以 <code>message</code> 的方式来管理数据的.</li>
<li>支持跨平台、跨语言，即[客户端和服务器端可以是不同的语言编写的]（支持目前绝大多数语言，例如 <code>C++</code>、<code>C#</code>、<code>Java</code>、<code>python</code> 等）</li>
<li>高性能，高可靠性</li>
<li>使用 <code>protobuf</code> 编译器能自动生成代码，<code>Protobuf</code> 是将类的定义使用 <code>.proto</code> 文件进行描述。说明，在 <code>idea</code> 中编写 <code>.proto</code> 文件时，会自动提示是否下载 <code>.ptoto</code> 编写插件.可以让语法高亮。</li>
<li>然后通过 <code>protoc.exe</code> 编译器根据 <code>.proto</code> 自动生成 <code>.java</code> 文件</li>
<li><code>protobuf</code> 使用示意图</li>
</ol>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0002.png"/>

<h2 id="Protobuf-快速入门实例"><a href="#Protobuf-快速入门实例" class="headerlink" title="Protobuf 快速入门实例"></a>Protobuf 快速入门实例</h2><p>编写程序，使用 <code>Protobuf</code> 完成如下功能</p>
<ol>
<li>客户端可以发送一个 <code>StudentPoJo</code> 对象到服务器(通过 <code>Protobuf</code> 编码)</li>
<li>服务端能接收 <code>StudentPoJo</code> 对象，并显示信息(通过 <code>Protobuf</code> 解码)</li>
</ol>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">&lt;dependency&gt;</span><br><span class="line">       &lt;groupId&gt;com.google.protobuf&lt;/groupId&gt;</span><br><span class="line">       &lt;artifactId&gt;protobuf-java&lt;/artifactId&gt;</span><br><span class="line">       &lt;version&gt;3.6.1&lt;/version&gt;</span><br><span class="line">   &lt;/dependency&gt;</span><br></pre></td></tr></table></figure>



<h3 id="Student-proto"><a href="#Student-proto" class="headerlink" title="Student.proto"></a>Student.proto</h3><figure class="highlight protobuf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">syntax = <span class="string">&quot;proto3&quot;</span>; <span class="comment">//版本</span></span><br><span class="line"><span class="keyword">option</span> java_outer_classname = <span class="string">&quot;StudentPOJO&quot;</span>;<span class="comment">//生成的外部类名，同时也是文件名</span></span><br><span class="line"><span class="comment">//protobuf 使用message 管理数据</span></span><br><span class="line"><span class="keyword">message </span><span class="title class_">Student</span> &#123; <span class="comment">//会在 StudentPOJO 外部类生成一个内部类 Student， 他是真正发送的POJO对象</span></span><br><span class="line">    <span class="type">int32</span> id = <span class="number">1</span>; <span class="comment">// Student 类中有 一个属性 名字为 id 类型为int32(protobuf类型) 1表示属性序号，不是值</span></span><br><span class="line">    <span class="type">string</span> name = <span class="number">2</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>编译<br>protoc.exe –java_out&#x3D;.Student.proto<br>将生成的 StudentPOJO 放入到项目使用</p>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0003.png"/>

<p>生成的StudentPOJO代码太长就不贴在这里了</p>
<h3 id="NettyServer"><a href="#NettyServer" class="headerlink" title="NettyServer"></a>NettyServer</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.codec;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> io.netty.bootstrap.ServerBootstrap;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.*;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.nio.NioEventLoopGroup;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.socket.SocketChannel;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.socket.nio.NioServerSocketChannel;</span><br><span class="line"><span class="keyword">import</span> io.netty.handler.codec.protobuf.ProtobufDecoder;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">NettyServer</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        <span class="comment">//创建BossGroup 和 WorkerGroup</span></span><br><span class="line">        <span class="comment">//说明</span></span><br><span class="line">        <span class="comment">//1. 创建两个线程组 bossGroup 和 workerGroup</span></span><br><span class="line">        <span class="comment">//2. bossGroup 只是处理连接请求 , 真正的和客户端业务处理，会交给 workerGroup完成</span></span><br><span class="line">        <span class="comment">//3. 两个都是无限循环</span></span><br><span class="line">        <span class="comment">//4. bossGroup 和 workerGroup 含有的子线程(NioEventLoop)的个数</span></span><br><span class="line">        <span class="comment">//   默认实际 cpu核数 * 2</span></span><br><span class="line">        <span class="type">EventLoopGroup</span> <span class="variable">bossGroup</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">NioEventLoopGroup</span>(<span class="number">1</span>);</span><br><span class="line">        <span class="type">EventLoopGroup</span> <span class="variable">workerGroup</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">NioEventLoopGroup</span>(); <span class="comment">//8</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">//创建服务器端的启动对象，配置参数</span></span><br><span class="line">            <span class="type">ServerBootstrap</span> <span class="variable">bootstrap</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ServerBootstrap</span>();</span><br><span class="line"></span><br><span class="line">            <span class="comment">//使用链式编程来进行设置</span></span><br><span class="line">            bootstrap.group(bossGroup, workerGroup) <span class="comment">//设置两个线程组</span></span><br><span class="line">                    .channel(NioServerSocketChannel.class) <span class="comment">//使用NioSocketChannel 作为服务器的通道实现</span></span><br><span class="line">                    .option(ChannelOption.SO_BACKLOG, <span class="number">128</span>) <span class="comment">// 设置线程队列得到连接个数</span></span><br><span class="line">                    .childOption(ChannelOption.SO_KEEPALIVE, <span class="literal">true</span>) <span class="comment">//设置保持活动连接状态</span></span><br><span class="line"><span class="comment">//                    .handler(null) // 该 handler对应 bossGroup , childHandler 对应 workerGroup</span></span><br><span class="line">                    .childHandler(<span class="keyword">new</span> <span class="title class_">ChannelInitializer</span>&lt;SocketChannel&gt;() &#123;<span class="comment">//创建一个通道初始化对象(匿名对象)</span></span><br><span class="line">                        <span class="comment">//给pipeline 设置处理器</span></span><br><span class="line">                        <span class="meta">@Override</span></span><br><span class="line">                        <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">initChannel</span><span class="params">(SocketChannel ch)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">                            <span class="type">ChannelPipeline</span> <span class="variable">pipeline</span> <span class="operator">=</span> ch.pipeline();</span><br><span class="line">                            <span class="comment">//在pipeline加入ProtoBufDecoder</span></span><br><span class="line">                            <span class="comment">//指定对哪种对象进行解码</span></span><br><span class="line">                            pipeline.addLast(<span class="string">&quot;decoder&quot;</span>, <span class="keyword">new</span> <span class="title class_">ProtobufDecoder</span>(StudentPOJO.Student.getDefaultInstance()));</span><br><span class="line">                            pipeline.addLast(<span class="keyword">new</span> <span class="title class_">NettyServerHandler</span>());</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;); <span class="comment">// 给我们的workerGroup 的 EventLoop 对应的管道设置处理器</span></span><br><span class="line"></span><br><span class="line">            System.out.println(<span class="string">&quot;.....服务器 is ready...&quot;</span>);</span><br><span class="line"></span><br><span class="line">            <span class="comment">//绑定一个端口并且同步, 生成了一个 ChannelFuture 对象</span></span><br><span class="line">            <span class="comment">//启动服务器(并绑定端口)</span></span><br><span class="line">            <span class="type">ChannelFuture</span> <span class="variable">cf</span> <span class="operator">=</span> bootstrap.bind(<span class="number">6668</span>).sync();</span><br><span class="line"></span><br><span class="line">            <span class="comment">//给cf 注册监听器，监控我们关心的事件</span></span><br><span class="line"></span><br><span class="line">            cf.addListener(<span class="keyword">new</span> <span class="title class_">ChannelFutureListener</span>() &#123;</span><br><span class="line">                <span class="meta">@Override</span></span><br><span class="line">                <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">operationComplete</span><span class="params">(ChannelFuture future)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">                    <span class="keyword">if</span> (cf.isSuccess()) &#123;</span><br><span class="line">                        System.out.println(<span class="string">&quot;监听端口 6668 成功&quot;</span>);</span><br><span class="line">                    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                        System.out.println(<span class="string">&quot;监听端口 6668 失败&quot;</span>);</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">            <span class="comment">//对关闭通道进行监听</span></span><br><span class="line">            cf.channel().closeFuture().sync();</span><br><span class="line">        &#125;<span class="keyword">finally</span> &#123;</span><br><span class="line">            bossGroup.shutdownGracefully();</span><br><span class="line">            workerGroup.shutdownGracefully();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>



<h3 id="NettyServerHandler"><a href="#NettyServerHandler" class="headerlink" title="NettyServerHandler"></a>NettyServerHandler</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.codec;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> io.netty.buffer.ByteBuf;</span><br><span class="line"><span class="keyword">import</span> io.netty.buffer.Unpooled;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.*;</span><br><span class="line"><span class="keyword">import</span> io.netty.util.CharsetUtil;</span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">说明</span></span><br><span class="line"><span class="comment">1. 我们自定义一个Handler 需要继续netty 规定好的某个HandlerAdapter(规范)</span></span><br><span class="line"><span class="comment">2. 这时我们自定义一个Handler , 才能称为一个handler</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="comment">//public class NettyServerHandler extends ChannelInboundHandlerAdapter &#123;</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">NettyServerHandler</span> <span class="keyword">extends</span> <span class="title class_">SimpleChannelInboundHandler</span>&lt;StudentPOJO.Student&gt; &#123;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">//读取数据实际(这里我们可以读取客户端发送的消息)</span></span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">    1. ChannelHandlerContext ctx:上下文对象, 含有 管道pipeline , 通道channel, 地址</span></span><br><span class="line"><span class="comment">    2. Object msg: 就是客户端发送的数据 默认Object</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">channelRead0</span><span class="params">(ChannelHandlerContext ctx, StudentPOJO.Student msg)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//读取从客户端发送的StudentPojo.Student</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;客户端发送的数据 id=&quot;</span> + msg.getId() + <span class="string">&quot; 名字=&quot;</span> + msg.getName());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//数据读取完毕</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">channelReadComplete</span><span class="params">(ChannelHandlerContext ctx)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//writeAndFlush 是 write + flush</span></span><br><span class="line">        <span class="comment">//将数据写入到缓存，并刷新</span></span><br><span class="line">        <span class="comment">//一般讲，我们对这个发送的数据进行编码</span></span><br><span class="line">        ctx.writeAndFlush(Unpooled.copiedBuffer(<span class="string">&quot;hello, 客户端~(&gt;^ω^&lt;)喵1&quot;</span>, CharsetUtil.UTF_8));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//处理异常, 一般是需要关闭通道</span></span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">exceptionCaught</span><span class="params">(ChannelHandlerContext ctx, Throwable cause)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        ctx.close();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>



<h3 id="NettyClient"><a href="#NettyClient" class="headerlink" title="NettyClient"></a>NettyClient</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.codec;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> io.netty.bootstrap.Bootstrap;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelFuture;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelInitializer;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelPipeline;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.EventLoopGroup;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.nio.NioEventLoopGroup;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.socket.SocketChannel;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.socket.nio.NioSocketChannel;</span><br><span class="line"><span class="keyword">import</span> io.netty.handler.codec.protobuf.ProtobufEncoder;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">NettyClient</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//客户端需要一个事件循环组</span></span><br><span class="line">        <span class="type">EventLoopGroup</span> <span class="variable">group</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">NioEventLoopGroup</span>();</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">//创建客户端启动对象</span></span><br><span class="line">            <span class="comment">//注意客户端使用的不是 ServerBootstrap 而是 Bootstrap</span></span><br><span class="line">            <span class="type">Bootstrap</span> <span class="variable">bootstrap</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Bootstrap</span>();</span><br><span class="line"></span><br><span class="line">            <span class="comment">//设置相关参数</span></span><br><span class="line">            bootstrap.group(group) <span class="comment">//设置线程组</span></span><br><span class="line">                    .channel(NioSocketChannel.class) <span class="comment">// 设置客户端通道的实现类(反射)</span></span><br><span class="line">                    .handler(<span class="keyword">new</span> <span class="title class_">ChannelInitializer</span>&lt;SocketChannel&gt;() &#123;</span><br><span class="line">                        <span class="meta">@Override</span></span><br><span class="line">                        <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">initChannel</span><span class="params">(SocketChannel ch)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">                            <span class="type">ChannelPipeline</span> <span class="variable">pipeline</span> <span class="operator">=</span> ch.pipeline();</span><br><span class="line">                            <span class="comment">//在pipeline中加入 ProtoBufEncoder</span></span><br><span class="line">                            pipeline.addLast(<span class="string">&quot;encoder&quot;</span>, <span class="keyword">new</span> <span class="title class_">ProtobufEncoder</span>());</span><br><span class="line">                            pipeline.addLast(<span class="keyword">new</span> <span class="title class_">NettyClientHandler</span>()); <span class="comment">//加入自己的处理器</span></span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;);</span><br><span class="line"></span><br><span class="line">            System.out.println(<span class="string">&quot;客户端 ok..&quot;</span>);</span><br><span class="line"></span><br><span class="line">            <span class="comment">//启动客户端去连接服务器端</span></span><br><span class="line">            <span class="comment">//关于 ChannelFuture 要分析，涉及到netty的异步模型</span></span><br><span class="line">            <span class="type">ChannelFuture</span> <span class="variable">channelFuture</span> <span class="operator">=</span> bootstrap.connect(<span class="string">&quot;127.0.0.1&quot;</span>, <span class="number">6668</span>).sync();</span><br><span class="line">            <span class="comment">//给关闭通道进行监听</span></span><br><span class="line">            channelFuture.channel().closeFuture().sync();</span><br><span class="line">        &#125;<span class="keyword">finally</span> &#123;</span><br><span class="line"></span><br><span class="line">            group.shutdownGracefully();</span><br><span class="line"></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>



<h3 id="NettyClientHandler"><a href="#NettyClientHandler" class="headerlink" title="NettyClientHandler"></a>NettyClientHandler</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.codec;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> io.netty.buffer.ByteBuf;</span><br><span class="line"><span class="keyword">import</span> io.netty.buffer.Unpooled;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelHandlerContext;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelInboundHandlerAdapter;</span><br><span class="line"><span class="keyword">import</span> io.netty.util.CharsetUtil;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">NettyClientHandler</span> <span class="keyword">extends</span> <span class="title class_">ChannelInboundHandlerAdapter</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//当通道就绪就会触发该方法</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">channelActive</span><span class="params">(ChannelHandlerContext ctx)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//发生一个Student 对象到服务器</span></span><br><span class="line"></span><br><span class="line">        StudentPOJO.<span class="type">Student</span> <span class="variable">student</span> <span class="operator">=</span> StudentPOJO.Student.newBuilder().setId(<span class="number">4</span>).setName(<span class="string">&quot;智多星 吴用&quot;</span>).build();</span><br><span class="line">        <span class="comment">//Teacher , Member ,Message</span></span><br><span class="line">        ctx.writeAndFlush(student);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//当通道有读取事件时，会触发</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">channelRead</span><span class="params">(ChannelHandlerContext ctx, Object msg)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        <span class="type">ByteBuf</span> <span class="variable">buf</span> <span class="operator">=</span> (ByteBuf) msg;</span><br><span class="line">        System.out.println(<span class="string">&quot;服务器回复的消息:&quot;</span> + buf.toString(CharsetUtil.UTF_8));</span><br><span class="line">        System.out.println(<span class="string">&quot;服务器的地址： &quot;</span>+ ctx.channel().remoteAddress());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">exceptionCaught</span><span class="params">(ChannelHandlerContext ctx, Throwable cause)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        cause.printStackTrace();</span><br><span class="line">        ctx.close();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>







<h2 id="Protobuf-快速入门实例-2"><a href="#Protobuf-快速入门实例-2" class="headerlink" title="Protobuf 快速入门实例 2"></a>Protobuf 快速入门实例 2</h2><ol>
<li>编写程序，使用 <code>Protobuf</code> 完成如下功能</li>
<li>客户端可以随机发送 <code>StudentPoJo</code> &#x2F; <code>WorkerPoJo</code> 对象到服务器(通过 <code>Protobuf</code> 编码)</li>
<li>服务端能接收 <code>StudentPoJo</code> &#x2F; <code>WorkerPoJo</code> 对象(需要判断是哪种类型)，并显示信息(通过 <code>Protobuf</code> 解码)</li>
</ol>
<h3 id="proto"><a href="#proto" class="headerlink" title="proto"></a>proto</h3><figure class="highlight protobuf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line">syntax = <span class="string">&quot;proto3&quot;</span>;</span><br><span class="line"><span class="keyword">option</span> optimize_for = SPEED; <span class="comment">// 加快解析</span></span><br><span class="line"><span class="keyword">option</span> java_package=<span class="string">&quot;com.atguigu.netty.codec2&quot;</span>;   <span class="comment">//指定生成到哪个包下</span></span><br><span class="line"><span class="keyword">option</span> java_outer_classname=<span class="string">&quot;MyDataInfo&quot;</span>; <span class="comment">// 外部类名, 文件名</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">1.protobuf 可以使用message 管理其他的message。最终决定使用哪一个message作为传输对象</span></span><br><span class="line"><span class="comment">2.假设你某个项目需要传输20个对象，你不可能新建20个proto文件吧。此时你就可以</span></span><br><span class="line"><span class="comment">在一个文件里定义20个message，最后再用一个总的message（比方说这里的MyMessage）</span></span><br><span class="line"><span class="comment">来决定在实际传输时真正需要传输哪一个对象</span></span><br><span class="line"><span class="comment">3.因为你实际传输的时候大部分情况传输的都是一个对象，所以下面用oneof进行了限制</span></span><br><span class="line"><span class="comment">4.是否可以传多个对象呢？我个人认为是可以的，比如可以通过map(目前我也不太了解proto的语法)</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">message </span><span class="title class_">MyMessage</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//定义一个枚举类型,DataType如果是0则表示一个Student对象实例，DataType这个名称自定义</span></span><br><span class="line">    <span class="keyword">enum </span><span class="title class_">DataType</span> &#123;</span><br><span class="line">        StudentType = <span class="number">0</span>; <span class="comment">//在proto3 要求enum的编号从0开始</span></span><br><span class="line">        WorkerType = <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//用data_type 来标识传的是哪一个枚举类型，这里才真正开始定义MyMessage的数据类型</span></span><br><span class="line">    DataType data_type = <span class="number">1</span>;  <span class="comment">//所有后面的数字都只是编号而已</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">    1.oneof关键字 表示每次枚举类型进行传输时，限制最多只能传输一个对象。</span></span><br><span class="line"><span class="comment">    dataBody名称也是自定义的</span></span><br><span class="line"><span class="comment">    2.为什么这里的序号是2呢？因为上面DataType data_type = 1  占了第一个序号了</span></span><br><span class="line"><span class="comment">    3.MyMessage里真正出现的类型只有两个</span></span><br><span class="line"><span class="comment">      ①DataType类型</span></span><br><span class="line"><span class="comment">      ②Student类型或者Worker类型（这两个在真正传输的时候只会有一个出现）</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">    <span class="keyword">oneof</span> dataBody &#123;</span><br><span class="line">        Student student = <span class="number">2</span>;  <span class="comment">//注意这后面的数字也都只是编号而已</span></span><br><span class="line">        Worker worker = <span class="number">3</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">message </span><span class="title class_">Student</span> &#123;</span><br><span class="line">    <span class="type">int32</span> id = <span class="number">1</span>;<span class="comment">//Student类的属性</span></span><br><span class="line">    <span class="type">string</span> name = <span class="number">2</span>; <span class="comment">//</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">message </span><span class="title class_">Worker</span> &#123;</span><br><span class="line">    <span class="type">string</span> name=<span class="number">1</span>;</span><br><span class="line">    <span class="type">int32</span> age=<span class="number">2</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="NettyServer-1"><a href="#NettyServer-1" class="headerlink" title="NettyServer"></a>NettyServer</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.codec2;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.atguigu.netty.codec.StudentPOJO;</span><br><span class="line"><span class="keyword">import</span> io.netty.bootstrap.ServerBootstrap;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.*;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.nio.NioEventLoopGroup;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.socket.SocketChannel;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.socket.nio.NioServerSocketChannel;</span><br><span class="line"><span class="keyword">import</span> io.netty.handler.codec.protobuf.ProtobufDecoder;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">NettyServer</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        </span><br><span class="line">        <span class="type">EventLoopGroup</span> <span class="variable">bossGroup</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">NioEventLoopGroup</span>(<span class="number">1</span>);</span><br><span class="line">        <span class="type">EventLoopGroup</span> <span class="variable">workerGroup</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">NioEventLoopGroup</span>(); <span class="comment">//8</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">//创建服务器端的启动对象，配置参数</span></span><br><span class="line">            <span class="type">ServerBootstrap</span> <span class="variable">bootstrap</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ServerBootstrap</span>();</span><br><span class="line"></span><br><span class="line">            <span class="comment">//使用链式编程来进行设置</span></span><br><span class="line">            bootstrap.group(bossGroup, workerGroup) <span class="comment">//设置两个线程组</span></span><br><span class="line">                    .channel(NioServerSocketChannel.class) <span class="comment">//使用NioSocketChannel 作为服务器的通道实现</span></span><br><span class="line">                    .option(ChannelOption.SO_BACKLOG, <span class="number">128</span>) <span class="comment">// 设置线程队列得到连接个数</span></span><br><span class="line">                    .childOption(ChannelOption.SO_KEEPALIVE, <span class="literal">true</span>) <span class="comment">//设置保持活动连接状态</span></span><br><span class="line"><span class="comment">//                    .handler(null) // 该 handler对应 bossGroup , childHandler 对应 workerGroup</span></span><br><span class="line">                    .childHandler(<span class="keyword">new</span> <span class="title class_">ChannelInitializer</span>&lt;SocketChannel&gt;() &#123;<span class="comment">//创建一个通道初始化对象(匿名对象)</span></span><br><span class="line">                        <span class="comment">//给pipeline 设置处理器</span></span><br><span class="line">                        <span class="meta">@Override</span></span><br><span class="line">                        <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">initChannel</span><span class="params">(SocketChannel ch)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">                            <span class="type">ChannelPipeline</span> <span class="variable">pipeline</span> <span class="operator">=</span> ch.pipeline();</span><br><span class="line">                            <span class="comment">//在pipeline加入ProtoBufDecoder</span></span><br><span class="line">                            <span class="comment">//指定对哪种对象进行解码</span></span><br><span class="line">                            pipeline.addLast(<span class="string">&quot;decoder&quot;</span>, <span class="keyword">new</span> <span class="title class_">ProtobufDecoder</span>(MyDataInfo.MyMessage.getDefaultInstance()));</span><br><span class="line">                            pipeline.addLast(<span class="keyword">new</span> <span class="title class_">NettyServerHandler</span>());</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;); <span class="comment">// 给我们的workerGroup 的 EventLoop 对应的管道设置处理器</span></span><br><span class="line"></span><br><span class="line">            System.out.println(<span class="string">&quot;.....服务器 is ready...&quot;</span>);</span><br><span class="line"></span><br><span class="line">            <span class="comment">//绑定一个端口并且同步, 生成了一个 ChannelFuture 对象</span></span><br><span class="line">            <span class="comment">//启动服务器(并绑定端口)</span></span><br><span class="line">            <span class="type">ChannelFuture</span> <span class="variable">cf</span> <span class="operator">=</span> bootstrap.bind(<span class="number">6668</span>).sync();</span><br><span class="line"></span><br><span class="line">            <span class="comment">//给cf 注册监听器，监控我们关心的事件</span></span><br><span class="line"></span><br><span class="line">            cf.addListener(<span class="keyword">new</span> <span class="title class_">ChannelFutureListener</span>() &#123;</span><br><span class="line">                <span class="meta">@Override</span></span><br><span class="line">                <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">operationComplete</span><span class="params">(ChannelFuture future)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">                    <span class="keyword">if</span> (cf.isSuccess()) &#123;</span><br><span class="line">                        System.out.println(<span class="string">&quot;监听端口 6668 成功&quot;</span>);</span><br><span class="line">                    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                        System.out.println(<span class="string">&quot;监听端口 6668 失败&quot;</span>);</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">            <span class="comment">//对关闭通道进行监听</span></span><br><span class="line">            cf.channel().closeFuture().sync();</span><br><span class="line">        &#125;<span class="keyword">finally</span> &#123;</span><br><span class="line">            bossGroup.shutdownGracefully();</span><br><span class="line">            workerGroup.shutdownGracefully();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>



<h3 id="NettyServerHandler-1"><a href="#NettyServerHandler-1" class="headerlink" title="NettyServerHandler"></a>NettyServerHandler</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.codec2;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.atguigu.netty.codec.StudentPOJO;</span><br><span class="line"><span class="keyword">import</span> io.netty.buffer.Unpooled;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelHandlerContext;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.SimpleChannelInboundHandler;</span><br><span class="line"><span class="keyword">import</span> io.netty.util.CharsetUtil;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//public class NettyServerHandler extends ChannelInboundHandlerAdapter &#123;</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">NettyServerHandler</span> <span class="keyword">extends</span> <span class="title class_">SimpleChannelInboundHandler</span>&lt;MyDataInfo.MyMessage&gt; &#123;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">//读取数据实际(这里我们可以读取客户端发送的消息)</span></span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">    1. ChannelHandlerContext ctx:上下文对象, 含有 管道pipeline , 通道channel, 地址</span></span><br><span class="line"><span class="comment">    2. Object msg: 就是客户端发送的数据 默认Object</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">channelRead0</span><span class="params">(ChannelHandlerContext ctx, MyDataInfo.MyMessage msg)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//根据dataType 来显示不同的信息</span></span><br><span class="line"></span><br><span class="line">        MyDataInfo.MyMessage.<span class="type">DataType</span> <span class="variable">dataType</span> <span class="operator">=</span> msg.getDataType();</span><br><span class="line">        <span class="keyword">if</span>(dataType == MyDataInfo.MyMessage.DataType.StudentType) &#123;</span><br><span class="line"></span><br><span class="line">            MyDataInfo.<span class="type">Student</span> <span class="variable">student</span> <span class="operator">=</span> msg.getStudent();</span><br><span class="line">            System.out.println(<span class="string">&quot;学生id=&quot;</span> + student.getId() + <span class="string">&quot; 学生名字=&quot;</span> + student.getName());</span><br><span class="line"></span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span>(dataType == MyDataInfo.MyMessage.DataType.WorkerType) &#123;</span><br><span class="line">            MyDataInfo.<span class="type">Worker</span> <span class="variable">worker</span> <span class="operator">=</span> msg.getWorker();</span><br><span class="line">            System.out.println(<span class="string">&quot;工人的名字=&quot;</span> + worker.getName() + <span class="string">&quot; 年龄=&quot;</span> + worker.getAge());</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;传输的类型不正确&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">//数据读取完毕</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">channelReadComplete</span><span class="params">(ChannelHandlerContext ctx)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//writeAndFlush 是 write + flush</span></span><br><span class="line">        <span class="comment">//将数据写入到缓存，并刷新</span></span><br><span class="line">        <span class="comment">//一般讲，我们对这个发送的数据进行编码</span></span><br><span class="line">        ctx.writeAndFlush(Unpooled.copiedBuffer(<span class="string">&quot;hello, 客户端~(&gt;^ω^&lt;)喵1&quot;</span>, CharsetUtil.UTF_8));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//处理异常, 一般是需要关闭通道</span></span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">exceptionCaught</span><span class="params">(ChannelHandlerContext ctx, Throwable cause)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        ctx.close();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>



<h3 id="NettyClient-1"><a href="#NettyClient-1" class="headerlink" title="NettyClient"></a>NettyClient</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.codec2;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> io.netty.bootstrap.Bootstrap;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelFuture;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelInitializer;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelPipeline;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.EventLoopGroup;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.nio.NioEventLoopGroup;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.socket.SocketChannel;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.socket.nio.NioSocketChannel;</span><br><span class="line"><span class="keyword">import</span> io.netty.handler.codec.protobuf.ProtobufEncoder;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">NettyClient</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//客户端需要一个事件循环组</span></span><br><span class="line">        <span class="type">EventLoopGroup</span> <span class="variable">group</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">NioEventLoopGroup</span>();</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">//创建客户端启动对象</span></span><br><span class="line">            <span class="comment">//注意客户端使用的不是 ServerBootstrap 而是 Bootstrap</span></span><br><span class="line">            <span class="type">Bootstrap</span> <span class="variable">bootstrap</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Bootstrap</span>();</span><br><span class="line"></span><br><span class="line">            <span class="comment">//设置相关参数</span></span><br><span class="line">            bootstrap.group(group) <span class="comment">//设置线程组</span></span><br><span class="line">                    .channel(NioSocketChannel.class) <span class="comment">// 设置客户端通道的实现类(反射)</span></span><br><span class="line">                    .handler(<span class="keyword">new</span> <span class="title class_">ChannelInitializer</span>&lt;SocketChannel&gt;() &#123;</span><br><span class="line">                        <span class="meta">@Override</span></span><br><span class="line">                        <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">initChannel</span><span class="params">(SocketChannel ch)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">                            <span class="type">ChannelPipeline</span> <span class="variable">pipeline</span> <span class="operator">=</span> ch.pipeline();</span><br><span class="line">                            <span class="comment">//在pipeline中加入 ProtoBufEncoder</span></span><br><span class="line">                            pipeline.addLast(<span class="string">&quot;encoder&quot;</span>, <span class="keyword">new</span> <span class="title class_">ProtobufEncoder</span>());</span><br><span class="line">                            pipeline.addLast(<span class="keyword">new</span> <span class="title class_">NettyClientHandler</span>()); <span class="comment">//加入自己的处理器</span></span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;);</span><br><span class="line"></span><br><span class="line">            System.out.println(<span class="string">&quot;客户端 ok..&quot;</span>);</span><br><span class="line"></span><br><span class="line">            <span class="comment">//启动客户端去连接服务器端</span></span><br><span class="line">            <span class="comment">//关于 ChannelFuture 要分析，涉及到netty的异步模型</span></span><br><span class="line">            <span class="type">ChannelFuture</span> <span class="variable">channelFuture</span> <span class="operator">=</span> bootstrap.connect(<span class="string">&quot;127.0.0.1&quot;</span>, <span class="number">6668</span>).sync();</span><br><span class="line">            <span class="comment">//给关闭通道进行监听</span></span><br><span class="line">            channelFuture.channel().closeFuture().sync();</span><br><span class="line">        &#125;<span class="keyword">finally</span> &#123;</span><br><span class="line"></span><br><span class="line">            group.shutdownGracefully();</span><br><span class="line"></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>



<h3 id="NettyClientHandler-1"><a href="#NettyClientHandler-1" class="headerlink" title="NettyClientHandler"></a>NettyClientHandler</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.codec2;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.atguigu.netty.codec.StudentPOJO;</span><br><span class="line"><span class="keyword">import</span> io.netty.buffer.ByteBuf;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelHandlerContext;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelInboundHandlerAdapter;</span><br><span class="line"><span class="keyword">import</span> io.netty.util.CharsetUtil;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.Random;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">NettyClientHandler</span> <span class="keyword">extends</span> <span class="title class_">ChannelInboundHandlerAdapter</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//当通道就绪就会触发该方法</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">channelActive</span><span class="params">(ChannelHandlerContext ctx)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//随机的发送Student 或者 Workder 对象</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">random</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Random</span>().nextInt(<span class="number">3</span>);</span><br><span class="line">        MyDataInfo.<span class="type">MyMessage</span> <span class="variable">myMessage</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span>(<span class="number">0</span> == random) &#123; <span class="comment">//发送Student 对象</span></span><br><span class="line"></span><br><span class="line">            myMessage = MyDataInfo.MyMessage.newBuilder().setDataType(MyDataInfo.MyMessage.DataType.StudentType).setStudent(MyDataInfo.Student.newBuilder().setId(<span class="number">5</span>).setName(<span class="string">&quot;玉麒麟 卢俊义&quot;</span>).build()).build();</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123; <span class="comment">// 发送一个Worker 对象</span></span><br><span class="line"></span><br><span class="line">            myMessage = MyDataInfo.MyMessage.newBuilder().setDataType(MyDataInfo.MyMessage.DataType.WorkerType).setWorker(MyDataInfo.Worker.newBuilder().setAge(<span class="number">20</span>).setName(<span class="string">&quot;老李&quot;</span>).build()).build();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        ctx.writeAndFlush(myMessage);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//当通道有读取事件时，会触发</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">channelRead</span><span class="params">(ChannelHandlerContext ctx, Object msg)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        <span class="type">ByteBuf</span> <span class="variable">buf</span> <span class="operator">=</span> (ByteBuf) msg;</span><br><span class="line">        System.out.println(<span class="string">&quot;服务器回复的消息:&quot;</span> + buf.toString(CharsetUtil.UTF_8));</span><br><span class="line">        System.out.println(<span class="string">&quot;服务器的地址： &quot;</span>+ ctx.channel().remoteAddress());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">exceptionCaught</span><span class="params">(ChannelHandlerContext ctx, Throwable cause)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        cause.printStackTrace();</span><br><span class="line">        ctx.close();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>



<h1 id="Netty-编解码器和-Handler-调用机制"><a href="#Netty-编解码器和-Handler-调用机制" class="headerlink" title="Netty 编解码器和 Handler 调用机制"></a>Netty 编解码器和 Handler 调用机制</h1><h2 id="基本说明"><a href="#基本说明" class="headerlink" title="基本说明"></a>基本说明</h2><ol>
<li><code>Netty</code> 的组件设计：<code>Netty</code> 的主要组件有 <code>Channel</code>、<code>EventLoop</code>、<code>ChannelFuture</code>、<code>ChannelHandler</code>、<code>ChannelPipe</code> 等</li>
<li><code>ChannelHandler</code> 充当了处理入站和出站数据的应用程序逻辑的容器。例如，实现 <code>ChannelInboundHandler</code> 接口（或 <code>ChannelInboundHandlerAdapter</code>），你就可以接收入站事件和数据，这些数据会被业务逻辑处理。当要给客户端发送响应时，也可以从 <code>ChannelInboundHandler</code> 冲刷数据。业务逻辑通常写在一个或者多个 <code>ChannelInboundHandler</code> 中。<code>ChannelOutboundHandler</code> 原理一样，只不过它是用来处理出站数据的</li>
<li><code>ChannelPipeline</code> 提供了 <code>ChannelHandler</code> 链的容器。以客户端应用程序为例，如果事件的运动方向是从客户端到服务端的，那么我们称这些事件为出站的，即客户端发送给服务端的数据会通过 <code>pipeline</code> 中的一系列 <code>ChannelOutboundHandler</code>，并被这些 <code>Handler</code> 处理，反之则称为入站的</li>
</ol>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0004.png"/>

<blockquote>
<p>出站，入站如果搞不清楚，看下面的<strong>Netty的handler链的调用机制</strong>，通过一个例子和图讲清楚</p>
</blockquote>
<h2 id="编码解码器"><a href="#编码解码器" class="headerlink" title="编码解码器"></a>编码解码器</h2><ol>
<li>当 <code>Netty</code> 发送或者接受一个消息的时候，就将会发生一次数据转换。入站消息会被解码：从字节转换为另一种格式（比如 <code>java</code> 对象）；如果是出站消息，它会被编码成字节。</li>
<li><code>Netty</code> 提供一系列实用的编解码器，他们都实现了 <code>ChannelInboundHadnler</code> 或者 <code>ChannelOutboundHandler</code> 接口。在这些类中，<code>channelRead</code> 方法已经被重写了。以入站为例，对于每个从入站 <code>Channel</code> 读取的消息，这个方法会被调用。随后，它将调用由解码器所提供的 <code>decode()</code> 方法进行解码，并将已经解码的字节转发给 <code>ChannelPipeline</code> 中的下一个 <code>ChannelInboundHandler</code>。</li>
</ol>
<h2 id="解码器-ByteToMessageDecoder"><a href="#解码器-ByteToMessageDecoder" class="headerlink" title="解码器 - ByteToMessageDecoder"></a>解码器 - ByteToMessageDecoder</h2><ol>
<li>关系继承图</li>
</ol>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0005.png"/>

<ol start="2">
<li>由于不可能知道远程节点是否会一次性发送一个完整的信息，<code>tcp</code> 有可能出现粘包拆包的问题，这个类会对入站数据进行缓冲，直到它准备好被处理.【后面有说TCP的粘包和拆包问题】</li>
<li>一个关于 <code>ByteToMessageDecoder</code> 实例分析</li>
</ol>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0006.png"/>





<h2 id="Netty的handler链的调用机制"><a href="#Netty的handler链的调用机制" class="headerlink" title="Netty的handler链的调用机制"></a>Netty的handler链的调用机制</h2><p>实例要求:</p>
<ol>
<li>使用自定义的编码器和解码器来说明 <code>Netty</code> 的 <code>handler</code> 调用机制<br>客户端发送 <code>long</code> -&gt; 服务器<br>服务端发送 <code>long</code> -&gt; 客户端</li>
</ol>
<blockquote>
<p> 读者可以看下这个图，带着这个图去看下面的例子。</p>
</blockquote>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0007.png"/>



<h3 id="MyServer"><a href="#MyServer" class="headerlink" title="MyServer"></a>MyServer</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.inboundhandlerandoutboundhandler;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> io.netty.bootstrap.ServerBootstrap;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelFuture;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.EventLoopGroup;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.nio.NioEventLoopGroup;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.socket.nio.NioServerSocketChannel;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyServer</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception&#123;</span><br><span class="line"></span><br><span class="line">        <span class="type">EventLoopGroup</span> <span class="variable">bossGroup</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">NioEventLoopGroup</span>(<span class="number">1</span>);</span><br><span class="line">        <span class="type">EventLoopGroup</span> <span class="variable">workerGroup</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">NioEventLoopGroup</span>();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line"></span><br><span class="line">            <span class="type">ServerBootstrap</span> <span class="variable">serverBootstrap</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ServerBootstrap</span>();</span><br><span class="line">            serverBootstrap.group(bossGroup,workerGroup).channel(NioServerSocketChannel.class).childHandler(<span class="keyword">new</span> <span class="title class_">MyServerInitializer</span>()); <span class="comment">//自定义一个初始化类</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">            <span class="type">ChannelFuture</span> <span class="variable">channelFuture</span> <span class="operator">=</span> serverBootstrap.bind(<span class="number">7000</span>).sync();</span><br><span class="line">            channelFuture.channel().closeFuture().sync();</span><br><span class="line"></span><br><span class="line">        &#125;<span class="keyword">finally</span> &#123;</span><br><span class="line">            bossGroup.shutdownGracefully();</span><br><span class="line">            workerGroup.shutdownGracefully();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>



<h3 id="MyServerInitializer"><a href="#MyServerInitializer" class="headerlink" title="MyServerInitializer"></a>MyServerInitializer</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.inboundhandlerandoutboundhandler;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelInitializer;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelPipeline;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.socket.SocketChannel;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyServerInitializer</span> <span class="keyword">extends</span> <span class="title class_">ChannelInitializer</span>&lt;SocketChannel&gt; &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">initChannel</span><span class="params">(SocketChannel ch)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">ChannelPipeline</span> <span class="variable">pipeline</span> <span class="operator">=</span> ch.pipeline();<span class="comment">//一会下断点</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">//入站的handler进行解码 MyByteToLongDecoder</span></span><br><span class="line">        pipeline.addLast(<span class="keyword">new</span> <span class="title class_">MyByteToLongDecoder</span>());</span><br><span class="line">        <span class="comment">//出站的handler进行编码</span></span><br><span class="line">        pipeline.addLast(<span class="keyword">new</span> <span class="title class_">MyLongToByteEncoder</span>());</span><br><span class="line">        <span class="comment">//自定义的handler 处理业务逻辑</span></span><br><span class="line">        pipeline.addLast(<span class="keyword">new</span> <span class="title class_">MyServerHandler</span>());</span><br><span class="line">        System.out.println(<span class="string">&quot;xx&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>



<h3 id="MyServerHandler"><a href="#MyServerHandler" class="headerlink" title="MyServerHandler"></a>MyServerHandler</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.inboundhandlerandoutboundhandler;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelHandlerContext;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.SimpleChannelInboundHandler;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyServerHandler</span> <span class="keyword">extends</span> <span class="title class_">SimpleChannelInboundHandler</span>&lt;Long&gt; &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">channelRead0</span><span class="params">(ChannelHandlerContext ctx, Long msg)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;从客户端&quot;</span> + ctx.channel().remoteAddress() + <span class="string">&quot; 读取到long &quot;</span> + msg);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//给客户端发送一个long</span></span><br><span class="line">        ctx.writeAndFlush(<span class="number">98765L</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">exceptionCaught</span><span class="params">(ChannelHandlerContext ctx, Throwable cause)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        cause.printStackTrace();</span><br><span class="line">        ctx.close();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>



<h3 id="MyClient"><a href="#MyClient" class="headerlink" title="MyClient"></a>MyClient</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.inboundhandlerandoutboundhandler;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> io.netty.bootstrap.Bootstrap;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelFuture;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.EventLoopGroup;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.nio.NioEventLoopGroup;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.socket.nio.NioSocketChannel;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyClient</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span>  <span class="keyword">throws</span>  Exception&#123;</span><br><span class="line"></span><br><span class="line">        <span class="type">EventLoopGroup</span> <span class="variable">group</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">NioEventLoopGroup</span>();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line"></span><br><span class="line">            <span class="type">Bootstrap</span> <span class="variable">bootstrap</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Bootstrap</span>();</span><br><span class="line">            bootstrap.group(group).channel(NioSocketChannel.class)</span><br><span class="line">                    .handler(<span class="keyword">new</span> <span class="title class_">MyClientInitializer</span>()); <span class="comment">//自定义一个初始化类</span></span><br><span class="line"></span><br><span class="line">            <span class="type">ChannelFuture</span> <span class="variable">channelFuture</span> <span class="operator">=</span> bootstrap.connect(<span class="string">&quot;localhost&quot;</span>, <span class="number">7000</span>).sync();</span><br><span class="line"></span><br><span class="line">            channelFuture.channel().closeFuture().sync();</span><br><span class="line"></span><br><span class="line">        &#125;<span class="keyword">finally</span> &#123;</span><br><span class="line">            group.shutdownGracefully();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>



<h3 id="MyClientInitializer"><a href="#MyClientInitializer" class="headerlink" title="MyClientInitializer"></a>MyClientInitializer</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.inboundhandlerandoutboundhandler;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelInitializer;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelPipeline;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.socket.SocketChannel;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyClientInitializer</span> <span class="keyword">extends</span> <span class="title class_">ChannelInitializer</span>&lt;SocketChannel&gt; &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">initChannel</span><span class="params">(SocketChannel ch)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        <span class="type">ChannelPipeline</span> <span class="variable">pipeline</span> <span class="operator">=</span> ch.pipeline();</span><br><span class="line"></span><br><span class="line">        <span class="comment">//加入一个出站的handler 对数据进行一个编码</span></span><br><span class="line">        pipeline.addLast(<span class="keyword">new</span> <span class="title class_">MyLongToByteEncoder</span>());</span><br><span class="line"></span><br><span class="line">        <span class="comment">//这时一个入站的解码器(入站handler )</span></span><br><span class="line">        pipeline.addLast(<span class="keyword">new</span> <span class="title class_">MyByteToLongDecoder</span>());</span><br><span class="line">        <span class="comment">//加入一个自定义的handler ， 处理业务</span></span><br><span class="line">        pipeline.addLast(<span class="keyword">new</span> <span class="title class_">MyClientHandler</span>());</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>



<h3 id="MyClientHandler"><a href="#MyClientHandler" class="headerlink" title="MyClientHandler"></a>MyClientHandler</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.inboundhandlerandoutboundhandler;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> io.netty.buffer.Unpooled;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelHandlerContext;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.SimpleChannelInboundHandler;</span><br><span class="line"><span class="keyword">import</span> io.netty.util.CharsetUtil;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.nio.charset.Charset;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyClientHandler</span>  <span class="keyword">extends</span> <span class="title class_">SimpleChannelInboundHandler</span>&lt;Long&gt; &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">channelRead0</span><span class="params">(ChannelHandlerContext ctx, Long msg)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;服务器的ip=&quot;</span> + ctx.channel().remoteAddress());</span><br><span class="line">        System.out.println(<span class="string">&quot;收到服务器消息=&quot;</span> + msg);</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//重写channelActive 发送数据</span></span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">channelActive</span><span class="params">(ChannelHandlerContext ctx)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;MyClientHandler 发送数据&quot;</span>);</span><br><span class="line">        <span class="comment">//ctx.writeAndFlush(Unpooled.copiedBuffer(&quot;&quot;))</span></span><br><span class="line">        ctx.writeAndFlush(<span class="number">123456L</span>); <span class="comment">//发送的是一个long</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>



<h3 id="MyByteToLongDecoder"><a href="#MyByteToLongDecoder" class="headerlink" title="MyByteToLongDecoder"></a>MyByteToLongDecoder</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.inboundhandlerandoutboundhandler;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> io.netty.buffer.ByteBuf;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelHandlerContext;</span><br><span class="line"><span class="keyword">import</span> io.netty.handler.codec.ByteToMessageDecoder;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.List;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyByteToLongDecoder</span> <span class="keyword">extends</span> <span class="title class_">ByteToMessageDecoder</span> &#123;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * decode 会根据接收的数据，被调用多次, 直到确定没有新的元素被添加到list</span></span><br><span class="line"><span class="comment">     * , 或者是ByteBuf 没有更多的可读字节为止</span></span><br><span class="line"><span class="comment">     * 如果list out 不为空，就会将list的内容传递给下一个 channelinboundhandler处理,</span></span><br><span class="line"><span class="comment">     * 该处理器的方法也会被调用多次</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> ctx 上下文对象</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> in 入站的 ByteBuf</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> out List 集合，将解码后的数据传给下一个handler</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> Exception</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">decode</span><span class="params">(ChannelHandlerContext ctx, ByteBuf in, List&lt;Object&gt; out)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;MyByteToLongDecoder 被调用&quot;</span>);</span><br><span class="line">        <span class="comment">//因为 long 8个字节, 需要判断有8个字节，才能读取一个long</span></span><br><span class="line">        <span class="keyword">if</span>(in.readableBytes() &gt;= <span class="number">8</span>) &#123;</span><br><span class="line">            out.add(in.readLong());</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>



<h3 id="MyLongToByteEncoder"><a href="#MyLongToByteEncoder" class="headerlink" title="MyLongToByteEncoder"></a>MyLongToByteEncoder</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.inboundhandlerandoutboundhandler;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> io.netty.buffer.ByteBuf;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelHandlerContext;</span><br><span class="line"><span class="keyword">import</span> io.netty.handler.codec.MessageToByteEncoder;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyLongToByteEncoder</span> <span class="keyword">extends</span> <span class="title class_">MessageToByteEncoder</span>&lt;Long&gt; &#123;</span><br><span class="line">    <span class="comment">//编码方法</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">encode</span><span class="params">(ChannelHandlerContext ctx, Long msg, ByteBuf out)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;MyLongToByteEncoder encode 被调用&quot;</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;msg=&quot;</span> + msg);</span><br><span class="line">        out.writeLong(msg);</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h3 id="效果"><a href="#效果" class="headerlink" title="效果"></a>效果</h3><img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0008.png"/>



<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0009.png"/>



<h3 id="出站入站"><a href="#出站入站" class="headerlink" title="出站入站"></a>出站入站</h3><p>关于出站入站，很多人可能有点迷糊<br>1）客户端有出站入站，服务端也有出站入站<br>2）以客户端为例，如果有服务端传送的数据到达客户端，那么对于客户端来说就是入站；<br>                             如果客户端传送数据到服务端，那么对于客户端来说就是出站；<br>                     同理，对于服务端来说，也是一样的，有数据来就是入站，有数据输出就是出站<br>3）为什么服务端和客户端的Serverhandler都是继承<code>SimpleChannelInboundHandler</code>，而没有<code>ChannelOutboundHandler</code>出站类？<br>      实际上当我们在handler中调用ctx.writeAndFlush()方法后，就会将数据交给ChannelOutboundHandler进行出站处理，只是我们没有去定义出站类而已，若有需求可以自己去实现ChannelOutboundHandler出站类<br>4）总结就是客户端和服务端都有出站和入站的操作<br>        <strong>服务端发数据给客户端：</strong>服务端—&gt;出站—&gt;Socket通道—&gt;入站—&gt;客户端</p>
<p>​        <strong>客户端发数据给服务端：</strong>客户端—&gt;出站—&gt;Socket通道—&gt;入站—&gt;服务端</p>
<p>​	    </p>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0007.png"/>



<h2 id="ByteToMessageDecoder的小细节"><a href="#ByteToMessageDecoder的小细节" class="headerlink" title="ByteToMessageDecoder的小细节"></a>ByteToMessageDecoder的小细节</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.inboundhandlerandoutboundhandler;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> io.netty.buffer.Unpooled;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelHandlerContext;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.SimpleChannelInboundHandler;</span><br><span class="line"><span class="keyword">import</span> io.netty.util.CharsetUtil;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.nio.charset.Charset;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyClientHandler</span>  <span class="keyword">extends</span> <span class="title class_">SimpleChannelInboundHandler</span>&lt;Long&gt; &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">channelRead0</span><span class="params">(ChannelHandlerContext ctx, Long msg)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;服务器的ip=&quot;</span> + ctx.channel().remoteAddress());</span><br><span class="line">        System.out.println(<span class="string">&quot;收到服务器消息=&quot;</span> + msg);</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//重写channelActive 发送数据</span></span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">channelActive</span><span class="params">(ChannelHandlerContext ctx)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;MyClientHandler 发送数据&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//分析</span></span><br><span class="line">        <span class="comment">//1. &quot;abcdabcdabcdabcd&quot; 是 16个字节</span></span><br><span class="line">        ctx.writeAndFlush(Unpooled.copiedBuffer(<span class="string">&quot;abcdabcdabcdabcd&quot;</span>,CharsetUtil.UTF_8));</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>



<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.inboundhandlerandoutboundhandler;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> io.netty.buffer.ByteBuf;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelHandlerContext;</span><br><span class="line"><span class="keyword">import</span> io.netty.handler.codec.ByteToMessageDecoder;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.List;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyByteToLongDecoder</span> <span class="keyword">extends</span> <span class="title class_">ByteToMessageDecoder</span> &#123;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * decode 会根据接收的数据，被调用多次, 直到确定没有新的元素被添加到list</span></span><br><span class="line"><span class="comment">     * , 或者是ByteBuf 没有更多的可读字节为止</span></span><br><span class="line"><span class="comment">     * 如果list out 不为空，就会将list的内容传递给下一个 channelinboundhandler处理,</span></span><br><span class="line"><span class="comment">     * 该处理器的方法也会被调用多次</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> ctx 上下文对象</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> in 入站的 ByteBuf</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> out List 集合，将解码后的数据传给下一个handler</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> Exception</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">decode</span><span class="params">(ChannelHandlerContext ctx, ByteBuf in, List&lt;Object&gt; out)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;MyByteToLongDecoder 被调用&quot;</span>);</span><br><span class="line">        <span class="comment">//因为 long 8个字节, 需要判断有8个字节，才能读取一个long</span></span><br><span class="line">        <span class="keyword">if</span>(in.readableBytes() &gt;= <span class="number">8</span>) &#123;</span><br><span class="line">            out.add(in.readLong());</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<ol>
<li>由于发送的字符串是16字节，根据上面注释说的内容，decode会被调用两次</li>
</ol>
<p>如下图验证结果：</p>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0010.png"/>



<ol start="2">
<li><p>同时又引出了一个小问题</p>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0011.png"/>

<p>当我们<code>MyClientHandler</code>传一个Long时，会调用我们的<code>MyLongToByteEncoder</code>的编码器。那么控制台就会打印这样一句话：<strong>MyLongToByteEncoder encode 被调用</strong>。但是这里并没有调用编码器，这是为什么呢？</p>
<ol>
<li><code>MyClientHandler</code>这个处理器的后一个处理器是<code>MyLongToByteEncoder</code></li>
<li><code>MyLongToByteEncoder</code>的父类是<code>MessageToByteEncoder</code>，在<code>MessageToByteEncoder</code>中有下面的一个方法</li>
</ol>
</li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">write</span><span class="params">(ChannelHandlerContext ctx, Object msg, ChannelPromise promise)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">ByteBuf</span> <span class="variable">buf</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">//这里会判断当前msg 是不是应该处理的类型，如果是就处理，不是就跳过encode</span></span><br><span class="line">            <span class="keyword">if</span> (acceptOutboundMessage(msg)) &#123;</span><br><span class="line">                <span class="meta">@SuppressWarnings(&quot;unchecked&quot;)</span></span><br><span class="line">                <span class="type">I</span> <span class="variable">cast</span> <span class="operator">=</span> (I) msg;</span><br><span class="line">                buf = allocateBuffer(ctx, cast, preferDirect);</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    encode(ctx, cast, buf);</span><br><span class="line">                &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                    ReferenceCountUtil.release(cast);</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                <span class="keyword">if</span> (buf.isReadable()) &#123;</span><br><span class="line">                    ctx.write(buf, promise);</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    buf.release();</span><br><span class="line">                    ctx.write(Unpooled.EMPTY_BUFFER, promise);</span><br><span class="line">                &#125;</span><br><span class="line">                buf = <span class="literal">null</span>;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                ctx.write(msg, promise);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (EncoderException e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> e;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Throwable e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">EncoderException</span>(e);</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (buf != <span class="literal">null</span>) &#123;</span><br><span class="line">                buf.release();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>

<p>​		</p>
<ol start="3">
<li>当我们以这样的形式发送数据</li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ctx.writeAndFlush(Unpooled.copiedBuffer(<span class="string">&quot;abcdabcdabcdabcd&quot;</span>,CharsetUtil.UTF_8));</span><br></pre></td></tr></table></figure>

<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0012.png" />



<p>这两个类型并不匹配，也就不会走编码器。因此我们编写 Encoder 是要注意传入的数据类型和处理的数据类型一致</p>
<p><strong>结论：</strong></p>
<ul>
<li>不论解码器 <code>handler</code> 还是编码器 <code>handler</code> 即接收的消息类型必须与待处理的消息类型一致，否则该 <code>handler</code> 不会被执行</li>
<li>在解码器进行数据解码时，需要判断缓存区（<code>ByteBuf</code>）的数据是否足够，否则接收到的结果会期望结果可能不一致。</li>
</ul>
<h2 id="解码器-ReplayingDecoder"><a href="#解码器-ReplayingDecoder" class="headerlink" title="解码器 - ReplayingDecoder"></a>解码器 - ReplayingDecoder</h2><ol>
<li><code>public abstract class ReplayingDecoder&lt;S&gt; extends ByteToMessageDecoder</code></li>
<li><code>ReplayingDecoder</code> 扩展了 <code>ByteToMessageDecoder</code> 类，使用这个类，我们不必调用 <code>readableBytes()</code> 方法，也就不用判断还有没有足够的数据来读取。参数 <code>S</code> 指定了用户状态管理的类型，其中 <code>Void</code> 代表不需要状态管理</li>
<li>应用实例：使用 <code>ReplayingDecoder</code> 编写解码器，对前面的案例进行简化[案例演示]</li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.inboundhandlerandoutboundhandler;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> io.netty.buffer.ByteBuf;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelHandlerContext;</span><br><span class="line"><span class="keyword">import</span> io.netty.handler.codec.ReplayingDecoder;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.List;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyByteToLongDecoder2</span> <span class="keyword">extends</span> <span class="title class_">ReplayingDecoder</span>&lt;Void&gt; &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">decode</span><span class="params">(ChannelHandlerContext ctx, ByteBuf in, List&lt;Object&gt; out)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;MyByteToLongDecoder2 被调用&quot;</span>);</span><br><span class="line">        <span class="comment">//在 ReplayingDecoder 不需要判断数据是否足够读取，内部会进行处理判断</span></span><br><span class="line">        out.add(in.readLong());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ol start="4">
<li><code>ReplayingDecoder</code> 使用方便，但它也有一些局限性：<ul>
<li>并不是所有的 <code>ByteBuf</code> 操作都被支持，如果调用了一个不被支持的方法，将会抛出一个 <code>UnsupportedOperationException</code>。</li>
<li><code>ReplayingDecoder</code> 在某些情况下可能稍慢于 <code>ByteToMessageDecoder</code>，例如网络缓慢并且消息格式复杂时，消息会被拆成了多个碎片，速度变慢</li>
</ul>
</li>
</ol>
<h2 id="其它编解码器"><a href="#其它编解码器" class="headerlink" title="其它编解码器"></a>其它编解码器</h2><img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0013.png"/>



<ol>
<li><code>LineBasedFrameDecoder</code>：这个类在 <code>Netty</code> 内部也有使用，它使用行尾控制字符（\n或者\r\n）作为分隔符来解析数据。</li>
<li><code>DelimiterBasedFrameDecoder</code>：使用自定义的特殊字符作为消息的分隔符。</li>
<li><code>HttpObjectDecoder</code>：一个 <code>HTTP</code> 数据的解码器</li>
<li><code>LengthFieldBasedFrameDecoder</code>：通过指定长度来标识整包消息，这样就可以自动的处理黏包和半包消息。</li>
</ol>
<h2 id="Log4j-整合到-Netty"><a href="#Log4j-整合到-Netty" class="headerlink" title="Log4j 整合到 Netty"></a>Log4j 整合到 Netty</h2><ol>
<li>在 <code>Maven</code> 中添加对 <code>Log4j</code> 的依赖在 <code>pom.xml</code></li>
</ol>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>log4j<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>log4j<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.2.17<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.slf4j<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>slf4j-api<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.7.25<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.slf4j<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>slf4j-log4j12<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.7.25<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">scope</span>&gt;</span>test<span class="tag">&lt;/<span class="name">scope</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.slf4j<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>slf4j-simple<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.7.25<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">scope</span>&gt;</span>test<span class="tag">&lt;/<span class="name">scope</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure>

<ol start="2">
<li>配置 <code>Log4j</code>，在 <code>resources/log4j.properties</code></li>
</ol>
<figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">log4j.rootLogger</span>=<span class="string">DEBUG,stdout</span></span><br><span class="line"><span class="attr">log4j.appender.stdout</span>=<span class="string">org.apache.log4j.ConsoleAppender</span></span><br><span class="line"><span class="attr">log4j.appender.stdout.layout</span>=<span class="string">org.apache.log4j.PatternLayout</span></span><br><span class="line"><span class="attr">log4j.appender.stdout.layout.ConversionPattern</span>=<span class="string">[%p]%C&#123;1&#125;-%m%n</span></span><br></pre></td></tr></table></figure>

<ol start="3">
<li>演示整合</li>
</ol>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0014.jpg"/>



<h1 id="TCP-粘包和拆包及解决方案"><a href="#TCP-粘包和拆包及解决方案" class="headerlink" title="TCP 粘包和拆包及解决方案"></a>TCP 粘包和拆包及解决方案</h1><h2 id="TCP-粘包和拆包基本介绍"><a href="#TCP-粘包和拆包基本介绍" class="headerlink" title="TCP 粘包和拆包基本介绍"></a>TCP 粘包和拆包基本介绍</h2><ol>
<li><code>TCP</code> 是面向连接的，面向流的，提供高可靠性服务。收发两端（客户端和服务器端）都要有一一成对的 <code>socket</code>，因此，发送端为了将多个发给接收端的包，更有效的发给对方，使用了优化方法（<code>Nagle</code> 算法），将多次间隔较小且数据量小的数据，合并成一个大的数据块，然后进行封包。这样做虽然提高了效率，但是接收端就难于分辨出完整的数据包了，因为面向流的通信是无消息保护边界的</li>
<li>由于 <code>TCP</code> 无消息保护边界,需要在接收端处理消息边界问题，也就是我们所说的粘包、拆包问题,看一张图</li>
<li><code>TCP</code> 粘包、拆包图解</li>
</ol>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0015.png"/>


<p>假设客户端分别发送了两个数据包 <code>D1</code> 和 <code>D2</code> 给服务端，由于服务端一次读取到字节数是不确定的，故可能存在以下四种情况：</p>
<ol>
<li>服务端分两次读取到了两个独立的数据包，分别是 <code>D1</code> 和 <code>D2</code>，没有粘包和拆包</li>
<li>服务端一次接受到了两个数据包，<code>D1</code> 和 <code>D2</code> 粘合在一起，称之为 <code>TCP</code> 粘包</li>
<li>服务端分两次读取到了数据包，第一次读取到了完整的 <code>D1</code> 包和 <code>D2</code> 包的部分内容，第二次读取到了 <code>D2</code> 包的剩余内容，这称之为 <code>TCP</code> 拆包</li>
<li>服务端分两次读取到了数据包，第一次读取到了 <code>D1</code> 包的部分内容 <code>D1_1</code>，第二次读取到了 <code>D1</code> 包的剩余部分内容 <code>D1_2</code> 和完整的 <code>D2</code> 包。</li>
</ol>
<h2 id="TCP-粘包和拆包现象实例"><a href="#TCP-粘包和拆包现象实例" class="headerlink" title="TCP 粘包和拆包现象实例"></a>TCP 粘包和拆包现象实例</h2><p>在编写 <code>Netty</code> 程序时，如果没有做处理，就会发生粘包和拆包的问题</p>
<p>看一个具体的实例：</p>
<h3 id="MyServer-1"><a href="#MyServer-1" class="headerlink" title="MyServer"></a>MyServer</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.tcp;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> io.netty.bootstrap.ServerBootstrap;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelFuture;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.EventLoopGroup;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.nio.NioEventLoopGroup;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.socket.nio.NioServerSocketChannel;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyServer</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception&#123;</span><br><span class="line"></span><br><span class="line">        <span class="type">EventLoopGroup</span> <span class="variable">bossGroup</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">NioEventLoopGroup</span>(<span class="number">1</span>);</span><br><span class="line">        <span class="type">EventLoopGroup</span> <span class="variable">workerGroup</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">NioEventLoopGroup</span>();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line"></span><br><span class="line">            <span class="type">ServerBootstrap</span> <span class="variable">serverBootstrap</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ServerBootstrap</span>();</span><br><span class="line">            serverBootstrap.group(bossGroup,workerGroup).channel(NioServerSocketChannel.class).childHandler(<span class="keyword">new</span> <span class="title class_">MyServerInitializer</span>()); <span class="comment">//自定义一个初始化类</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">            <span class="type">ChannelFuture</span> <span class="variable">channelFuture</span> <span class="operator">=</span> serverBootstrap.bind(<span class="number">7000</span>).sync();</span><br><span class="line">            channelFuture.channel().closeFuture().sync();</span><br><span class="line"></span><br><span class="line">        &#125;<span class="keyword">finally</span> &#123;</span><br><span class="line">            bossGroup.shutdownGracefully();</span><br><span class="line">            workerGroup.shutdownGracefully();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>



<h3 id="MyServerInitializer-1"><a href="#MyServerInitializer-1" class="headerlink" title="MyServerInitializer"></a>MyServerInitializer</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.tcp;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelInitializer;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelPipeline;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.socket.SocketChannel;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyServerInitializer</span> <span class="keyword">extends</span> <span class="title class_">ChannelInitializer</span>&lt;SocketChannel&gt; &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">initChannel</span><span class="params">(SocketChannel ch)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">ChannelPipeline</span> <span class="variable">pipeline</span> <span class="operator">=</span> ch.pipeline();</span><br><span class="line"></span><br><span class="line">        pipeline.addLast(<span class="keyword">new</span> <span class="title class_">MyServerHandler</span>());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>



<h3 id="MyServerHandler-1"><a href="#MyServerHandler-1" class="headerlink" title="MyServerHandler"></a>MyServerHandler</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.tcp;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> io.netty.buffer.ByteBuf;</span><br><span class="line"><span class="keyword">import</span> io.netty.buffer.Unpooled;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelHandlerContext;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.SimpleChannelInboundHandler;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.nio.charset.Charset;</span><br><span class="line"><span class="keyword">import</span> java.util.UUID;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyServerHandler</span> <span class="keyword">extends</span> <span class="title class_">SimpleChannelInboundHandler</span>&lt;ByteBuf&gt;&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> count;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">exceptionCaught</span><span class="params">(ChannelHandlerContext ctx, Throwable cause)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="comment">//cause.printStackTrace();</span></span><br><span class="line">        ctx.close();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">channelRead0</span><span class="params">(ChannelHandlerContext ctx, ByteBuf msg)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        <span class="type">byte</span>[] buffer = <span class="keyword">new</span> <span class="title class_">byte</span>[msg.readableBytes()];</span><br><span class="line">        msg.readBytes(buffer);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//将buffer转成字符串</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">message</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">String</span>(buffer, Charset.forName(<span class="string">&quot;utf-8&quot;</span>));</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;服务器接收到数据 &quot;</span> + message);</span><br><span class="line">        System.out.println(<span class="string">&quot;服务器接收到消息量=&quot;</span> + (++<span class="built_in">this</span>.count));</span><br><span class="line"></span><br><span class="line">        <span class="comment">//服务器回送数据给客户端, 回送一个随机id ,</span></span><br><span class="line">        <span class="type">ByteBuf</span> <span class="variable">responseByteBuf</span> <span class="operator">=</span> Unpooled.copiedBuffer(UUID.randomUUID().toString() + <span class="string">&quot; &quot;</span>, Charset.forName(<span class="string">&quot;utf-8&quot;</span>));</span><br><span class="line">        ctx.writeAndFlush(responseByteBuf);</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>



<h3 id="MyClient-1"><a href="#MyClient-1" class="headerlink" title="MyClient"></a>MyClient</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.tcp;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> io.netty.bootstrap.Bootstrap;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelFuture;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.EventLoopGroup;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.nio.NioEventLoopGroup;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.socket.nio.NioSocketChannel;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyClient</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span>  <span class="keyword">throws</span>  Exception&#123;</span><br><span class="line"></span><br><span class="line">        <span class="type">EventLoopGroup</span> <span class="variable">group</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">NioEventLoopGroup</span>();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line"></span><br><span class="line">            <span class="type">Bootstrap</span> <span class="variable">bootstrap</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Bootstrap</span>();</span><br><span class="line">            bootstrap.group(group).channel(NioSocketChannel.class)</span><br><span class="line">                    .handler(<span class="keyword">new</span> <span class="title class_">MyClientInitializer</span>()); <span class="comment">//自定义一个初始化类</span></span><br><span class="line"></span><br><span class="line">            <span class="type">ChannelFuture</span> <span class="variable">channelFuture</span> <span class="operator">=</span> bootstrap.connect(<span class="string">&quot;localhost&quot;</span>, <span class="number">7000</span>).sync();</span><br><span class="line"></span><br><span class="line">            channelFuture.channel().closeFuture().sync();</span><br><span class="line"></span><br><span class="line">        &#125;<span class="keyword">finally</span> &#123;</span><br><span class="line">            group.shutdownGracefully();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>



<h3 id="MyClientInitializer-1"><a href="#MyClientInitializer-1" class="headerlink" title="MyClientInitializer"></a>MyClientInitializer</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.tcp;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelInitializer;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelPipeline;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.socket.SocketChannel;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyClientInitializer</span> <span class="keyword">extends</span> <span class="title class_">ChannelInitializer</span>&lt;SocketChannel&gt; &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">initChannel</span><span class="params">(SocketChannel ch)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        <span class="type">ChannelPipeline</span> <span class="variable">pipeline</span> <span class="operator">=</span> ch.pipeline();</span><br><span class="line">        pipeline.addLast(<span class="keyword">new</span> <span class="title class_">MyClientHandler</span>());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>



<h3 id="MyClientHandler-1"><a href="#MyClientHandler-1" class="headerlink" title="MyClientHandler"></a>MyClientHandler</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.tcp;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> io.netty.buffer.ByteBuf;</span><br><span class="line"><span class="keyword">import</span> io.netty.buffer.Unpooled;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelHandlerContext;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.SimpleChannelInboundHandler;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.nio.charset.Charset;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyClientHandler</span> <span class="keyword">extends</span> <span class="title class_">SimpleChannelInboundHandler</span>&lt;ByteBuf&gt; &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> count;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">channelActive</span><span class="params">(ChannelHandlerContext ctx)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="comment">//使用客户端发送10条数据 hello,server 编号</span></span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i= <span class="number">0</span>; i&lt; <span class="number">10</span>; ++i) &#123;</span><br><span class="line">            <span class="type">ByteBuf</span> <span class="variable">buffer</span> <span class="operator">=</span> Unpooled.copiedBuffer(<span class="string">&quot;hello,server &quot;</span> + i, Charset.forName(<span class="string">&quot;utf-8&quot;</span>));</span><br><span class="line">            ctx.writeAndFlush(buffer);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">channelRead0</span><span class="params">(ChannelHandlerContext ctx, ByteBuf msg)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">byte</span>[] buffer = <span class="keyword">new</span> <span class="title class_">byte</span>[msg.readableBytes()];</span><br><span class="line">        msg.readBytes(buffer);</span><br><span class="line"></span><br><span class="line">        <span class="type">String</span> <span class="variable">message</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">String</span>(buffer, Charset.forName(<span class="string">&quot;utf-8&quot;</span>));</span><br><span class="line">        System.out.println(<span class="string">&quot;客户端接收到消息=&quot;</span> + message);</span><br><span class="line">        System.out.println(<span class="string">&quot;客户端接收消息数量=&quot;</span> + (++<span class="built_in">this</span>.count));</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">exceptionCaught</span><span class="params">(ChannelHandlerContext ctx, Throwable cause)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        cause.printStackTrace();</span><br><span class="line">        ctx.close();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>





<h3 id="效果-1"><a href="#效果-1" class="headerlink" title="效果"></a>效果</h3><p><strong>第一次运行：</strong></p>
<p><strong>Client</strong></p>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0016.png"/>

<p><strong>Server</strong></p>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0017.png"/>



<p><strong>第二次运行：</strong></p>
<p><strong>Client</strong></p>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0018.png"/>



<p><strong>Server</strong></p>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0019.png"/>



<p>可以看到第一次运行时，服务器一次性将10个数据都接收了，第二次运行时分六次接收的，这就很形象的看出了TCP的粘包现象。</p>
<h2 id="TCP-粘包和拆包解决方案"><a href="#TCP-粘包和拆包解决方案" class="headerlink" title="TCP 粘包和拆包解决方案"></a>TCP 粘包和拆包解决方案</h2><ol>
<li>常用方案：使用自定义协议+编解码器来解决</li>
<li>关键就是要解决服务器端每次读取数据长度的问题，这个问题解决，就不会出现服务器多读或少读数据的问题，从而避免的 <code>TCP</code> 粘包、拆包。</li>
</ol>
<p><strong>看一个具体的实例</strong></p>
<ol>
<li>要求客户端发送 <code>5</code> 个 <code>Message</code> 对象，客户端每次发送一个 <code>Message</code> 对象</li>
<li>服务器端每次接收一个 <code>Message</code>，分 <code>5</code> 次进行解码，每读取到一个 <code>Message</code>，会回复一个 <code>Message</code> 对象给客户端。</li>
</ol>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0020.png"/>



<h3 id="MessageProtocol"><a href="#MessageProtocol" class="headerlink" title="MessageProtocol"></a>MessageProtocol</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.protocoltcp;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//协议包</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MessageProtocol</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> len; <span class="comment">//关键</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">byte</span>[] content;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getLen</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> len;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setLen</span><span class="params">(<span class="type">int</span> len)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.len = len;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">byte</span>[] getContent() &#123;</span><br><span class="line">        <span class="keyword">return</span> content;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setContent</span><span class="params">(<span class="type">byte</span>[] content)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.content = content;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>



<h3 id="MyServer-2"><a href="#MyServer-2" class="headerlink" title="MyServer"></a>MyServer</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.protocoltcp;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> io.netty.bootstrap.ServerBootstrap;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelFuture;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.EventLoopGroup;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.nio.NioEventLoopGroup;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.socket.nio.NioServerSocketChannel;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyServer</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception&#123;</span><br><span class="line"></span><br><span class="line">        <span class="type">EventLoopGroup</span> <span class="variable">bossGroup</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">NioEventLoopGroup</span>(<span class="number">1</span>);</span><br><span class="line">        <span class="type">EventLoopGroup</span> <span class="variable">workerGroup</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">NioEventLoopGroup</span>();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line"></span><br><span class="line">            <span class="type">ServerBootstrap</span> <span class="variable">serverBootstrap</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ServerBootstrap</span>();</span><br><span class="line">            serverBootstrap.group(bossGroup,workerGroup).channel(NioServerSocketChannel.class).childHandler(<span class="keyword">new</span> <span class="title class_">MyServerInitializer</span>()); <span class="comment">//自定义一个初始化类</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">            <span class="type">ChannelFuture</span> <span class="variable">channelFuture</span> <span class="operator">=</span> serverBootstrap.bind(<span class="number">7000</span>).sync();</span><br><span class="line">            channelFuture.channel().closeFuture().sync();</span><br><span class="line"></span><br><span class="line">        &#125;<span class="keyword">finally</span> &#123;</span><br><span class="line">            bossGroup.shutdownGracefully();</span><br><span class="line">            workerGroup.shutdownGracefully();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>



<h3 id="MyServerInitializer-2"><a href="#MyServerInitializer-2" class="headerlink" title="MyServerInitializer"></a>MyServerInitializer</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.protocoltcp;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelInitializer;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelPipeline;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.socket.SocketChannel;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyServerInitializer</span> <span class="keyword">extends</span> <span class="title class_">ChannelInitializer</span>&lt;SocketChannel&gt; &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">initChannel</span><span class="params">(SocketChannel ch)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">ChannelPipeline</span> <span class="variable">pipeline</span> <span class="operator">=</span> ch.pipeline();</span><br><span class="line"></span><br><span class="line">        pipeline.addLast(<span class="keyword">new</span> <span class="title class_">MyMessageDecoder</span>());<span class="comment">//解码器</span></span><br><span class="line">        pipeline.addLast(<span class="keyword">new</span> <span class="title class_">MyMessageEncoder</span>());<span class="comment">//编码器</span></span><br><span class="line">        pipeline.addLast(<span class="keyword">new</span> <span class="title class_">MyServerHandler</span>());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>



<h3 id="MyServerHandler-2"><a href="#MyServerHandler-2" class="headerlink" title="MyServerHandler"></a>MyServerHandler</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.protocoltcp;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> io.netty.buffer.ByteBuf;</span><br><span class="line"><span class="keyword">import</span> io.netty.buffer.Unpooled;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelHandlerContext;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.SimpleChannelInboundHandler;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.nio.charset.Charset;</span><br><span class="line"><span class="keyword">import</span> java.util.UUID;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//处理业务的handler</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyServerHandler</span> <span class="keyword">extends</span> <span class="title class_">SimpleChannelInboundHandler</span>&lt;MessageProtocol&gt;&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> count;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">exceptionCaught</span><span class="params">(ChannelHandlerContext ctx, Throwable cause)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="comment">//cause.printStackTrace();</span></span><br><span class="line">        ctx.close();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">channelRead0</span><span class="params">(ChannelHandlerContext ctx, MessageProtocol msg)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//接收到数据，并处理</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">len</span> <span class="operator">=</span> msg.getLen();</span><br><span class="line">        <span class="type">byte</span>[] content = msg.getContent();</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;服务器接收到信息如下&quot;</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;长度=&quot;</span> + len);</span><br><span class="line">        System.out.println(<span class="string">&quot;内容=&quot;</span> + <span class="keyword">new</span> <span class="title class_">String</span>(content, Charset.forName(<span class="string">&quot;utf-8&quot;</span>)));</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;服务器接收到消息包数量=&quot;</span> + (++<span class="built_in">this</span>.count));</span><br><span class="line"></span><br><span class="line">        <span class="comment">//回复消息</span></span><br><span class="line">        System.out.println(<span class="string">&quot;服务端开始回复消息------&quot;</span>);</span><br><span class="line">        <span class="type">String</span> <span class="variable">responseContent</span> <span class="operator">=</span> UUID.randomUUID().toString();</span><br><span class="line">        <span class="type">int</span> <span class="variable">responseLen</span> <span class="operator">=</span> responseContent.getBytes(<span class="string">&quot;utf-8&quot;</span>).length;</span><br><span class="line">        <span class="type">byte</span>[]  responseContent2 = responseContent.getBytes(<span class="string">&quot;utf-8&quot;</span>);</span><br><span class="line">        <span class="comment">//构建一个协议包</span></span><br><span class="line">        <span class="type">MessageProtocol</span> <span class="variable">messageProtocol</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MessageProtocol</span>();</span><br><span class="line">        messageProtocol.setLen(responseLen);</span><br><span class="line">        messageProtocol.setContent(responseContent2);</span><br><span class="line"></span><br><span class="line">        ctx.writeAndFlush(messageProtocol);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>



<h3 id="MyClient-2"><a href="#MyClient-2" class="headerlink" title="MyClient"></a>MyClient</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.protocoltcp;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> io.netty.bootstrap.Bootstrap;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelFuture;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.EventLoopGroup;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.nio.NioEventLoopGroup;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.socket.nio.NioSocketChannel;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyClient</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span>  <span class="keyword">throws</span>  Exception&#123;</span><br><span class="line"></span><br><span class="line">        <span class="type">EventLoopGroup</span> <span class="variable">group</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">NioEventLoopGroup</span>();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line"></span><br><span class="line">            <span class="type">Bootstrap</span> <span class="variable">bootstrap</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Bootstrap</span>();</span><br><span class="line">            bootstrap.group(group).channel(NioSocketChannel.class)</span><br><span class="line">                    .handler(<span class="keyword">new</span> <span class="title class_">MyClientInitializer</span>()); <span class="comment">//自定义一个初始化类</span></span><br><span class="line"></span><br><span class="line">            <span class="type">ChannelFuture</span> <span class="variable">channelFuture</span> <span class="operator">=</span> bootstrap.connect(<span class="string">&quot;localhost&quot;</span>, <span class="number">7000</span>).sync();</span><br><span class="line"></span><br><span class="line">            channelFuture.channel().closeFuture().sync();</span><br><span class="line"></span><br><span class="line">        &#125;<span class="keyword">finally</span> &#123;</span><br><span class="line">            group.shutdownGracefully();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>



<h3 id="MyClientInitializer-2"><a href="#MyClientInitializer-2" class="headerlink" title="MyClientInitializer"></a>MyClientInitializer</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.protocoltcp;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelInitializer;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelPipeline;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.socket.SocketChannel;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyClientInitializer</span> <span class="keyword">extends</span> <span class="title class_">ChannelInitializer</span>&lt;SocketChannel&gt; &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">initChannel</span><span class="params">(SocketChannel ch)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        <span class="type">ChannelPipeline</span> <span class="variable">pipeline</span> <span class="operator">=</span> ch.pipeline();</span><br><span class="line">        pipeline.addLast(<span class="keyword">new</span> <span class="title class_">MyMessageEncoder</span>()); <span class="comment">//加入编码器</span></span><br><span class="line">        pipeline.addLast(<span class="keyword">new</span> <span class="title class_">MyMessageDecoder</span>()); <span class="comment">//加入解码器</span></span><br><span class="line">        pipeline.addLast(<span class="keyword">new</span> <span class="title class_">MyClientHandler</span>());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>



<h3 id="MyClientHandler-2"><a href="#MyClientHandler-2" class="headerlink" title="MyClientHandler"></a>MyClientHandler</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.protocoltcp;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> io.netty.buffer.ByteBuf;</span><br><span class="line"><span class="keyword">import</span> io.netty.buffer.Unpooled;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelHandlerContext;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.SimpleChannelInboundHandler;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.nio.charset.Charset;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyClientHandler</span> <span class="keyword">extends</span> <span class="title class_">SimpleChannelInboundHandler</span>&lt;MessageProtocol&gt; &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> count;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">channelActive</span><span class="params">(ChannelHandlerContext ctx)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="comment">//使用客户端发送10条数据 &quot;今天天气冷，吃火锅&quot; 编号</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i&lt; <span class="number">5</span>; i++) &#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">mes</span> <span class="operator">=</span> <span class="string">&quot;今天天气冷，吃火锅&quot;</span>;</span><br><span class="line">            <span class="type">byte</span>[] content = mes.getBytes(Charset.forName(<span class="string">&quot;utf-8&quot;</span>));</span><br><span class="line">            <span class="type">int</span> <span class="variable">length</span> <span class="operator">=</span> mes.getBytes(Charset.forName(<span class="string">&quot;utf-8&quot;</span>)).length;</span><br><span class="line"></span><br><span class="line">            <span class="comment">//创建协议包对象</span></span><br><span class="line">            <span class="type">MessageProtocol</span> <span class="variable">messageProtocol</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MessageProtocol</span>();</span><br><span class="line">            messageProtocol.setLen(length);</span><br><span class="line">            messageProtocol.setContent(content);</span><br><span class="line">            ctx.writeAndFlush(messageProtocol);</span><br><span class="line"></span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//    @Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">channelRead0</span><span class="params">(ChannelHandlerContext ctx, MessageProtocol msg)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">        <span class="type">int</span> <span class="variable">len</span> <span class="operator">=</span> msg.getLen();</span><br><span class="line">        <span class="type">byte</span>[] content = msg.getContent();</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;客户端接收到消息如下&quot;</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;长度=&quot;</span> + len);</span><br><span class="line">        System.out.println(<span class="string">&quot;内容=&quot;</span> + <span class="keyword">new</span> <span class="title class_">String</span>(content, Charset.forName(<span class="string">&quot;utf-8&quot;</span>)));</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;客户端接收消息数量=&quot;</span> + (++<span class="built_in">this</span>.count));</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">exceptionCaught</span><span class="params">(ChannelHandlerContext ctx, Throwable cause)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;异常消息=&quot;</span> + cause.getMessage());</span><br><span class="line">        ctx.close();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>



<h3 id="MyMessageDecoder"><a href="#MyMessageDecoder" class="headerlink" title="MyMessageDecoder"></a>MyMessageDecoder</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.protocoltcp;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> io.netty.buffer.ByteBuf;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelHandlerContext;</span><br><span class="line"><span class="keyword">import</span> io.netty.handler.codec.ReplayingDecoder;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.List;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyMessageDecoder</span> <span class="keyword">extends</span> <span class="title class_">ReplayingDecoder</span>&lt;Void&gt; &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">decode</span><span class="params">(ChannelHandlerContext ctx, ByteBuf in, List&lt;Object&gt; out)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        System.out.println();</span><br><span class="line">        System.out.println();</span><br><span class="line">        System.out.println(<span class="string">&quot;MyMessageDecoder decode 被调用&quot;</span>);</span><br><span class="line">        <span class="comment">//需要将得到二进制字节码-&gt; MessageProtocol 数据包(对象)</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">length</span> <span class="operator">=</span> in.readInt();</span><br><span class="line"></span><br><span class="line">        <span class="type">byte</span>[] content = <span class="keyword">new</span> <span class="title class_">byte</span>[length];</span><br><span class="line">        in.readBytes(content);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//封装成 MessageProtocol 对象，放入 out， 传递下一个handler业务处理</span></span><br><span class="line">        <span class="type">MessageProtocol</span> <span class="variable">messageProtocol</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MessageProtocol</span>();</span><br><span class="line">        messageProtocol.setLen(length);</span><br><span class="line">        messageProtocol.setContent(content);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//放入out传给下一个hanlder进行处理</span></span><br><span class="line">        out.add(messageProtocol);</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>



<h3 id="MyMessageEncoder"><a href="#MyMessageEncoder" class="headerlink" title="MyMessageEncoder"></a>MyMessageEncoder</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.protocoltcp;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> io.netty.buffer.ByteBuf;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelHandlerContext;</span><br><span class="line"><span class="keyword">import</span> io.netty.handler.codec.MessageToByteEncoder;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyMessageEncoder</span> <span class="keyword">extends</span> <span class="title class_">MessageToByteEncoder</span>&lt;MessageProtocol&gt; &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">encode</span><span class="params">(ChannelHandlerContext ctx, MessageProtocol msg, ByteBuf out)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;MyMessageEncoder encode 方法被调用&quot;</span>);</span><br><span class="line">        out.writeInt(msg.getLen());</span><br><span class="line">        out.writeBytes(msg.getContent());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>



<h3 id="效果-2"><a href="#效果-2" class="headerlink" title="效果"></a>效果</h3><p><strong>Client输出</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line">MyMessageEncoder encode 方法被调用</span><br><span class="line">MyMessageEncoder encode 方法被调用</span><br><span class="line">MyMessageEncoder encode 方法被调用</span><br><span class="line">MyMessageEncoder encode 方法被调用</span><br><span class="line">MyMessageEncoder encode 方法被调用</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//下面是客户端开始一个一个的收到服务端的回复</span></span><br><span class="line">MyMessageDecoder decode 被调用</span><br><span class="line">客户端接收到消息如下</span><br><span class="line">长度=<span class="number">36</span></span><br><span class="line">内容=1b5286dd-0fc2-4f62-9bf7-d5fad84179b5</span><br><span class="line">客户端接收消息数量=<span class="number">1</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">MyMessageDecoder decode 被调用</span><br><span class="line">客户端接收到消息如下</span><br><span class="line">长度=<span class="number">36</span></span><br><span class="line">内容=653d18cb-ab72-<span class="number">4163</span>-8b95-09c94ecac873</span><br><span class="line">客户端接收消息数量=<span class="number">2</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">MyMessageDecoder decode 被调用</span><br><span class="line">客户端接收到消息如下</span><br><span class="line">长度=<span class="number">36</span></span><br><span class="line">内容=3be6e403-91bb-<span class="number">4437</span>-ada8-6cdb9eb7ef00</span><br><span class="line">客户端接收消息数量=<span class="number">3</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">MyMessageDecoder decode 被调用</span><br><span class="line">客户端接收到消息如下</span><br><span class="line">长度=<span class="number">36</span></span><br><span class="line">内容=94c8f306-fd9c-455a-956c-16698ce4150b</span><br><span class="line">客户端接收消息数量=<span class="number">4</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">MyMessageDecoder decode 被调用</span><br><span class="line">客户端接收到消息如下</span><br><span class="line">长度=<span class="number">36</span></span><br><span class="line">内容=7890de9c-0fa2-<span class="number">4317</span>-8de1-1d464315fa1b</span><br><span class="line">客户端接收消息数量=<span class="number">5</span></span><br><span class="line"></span><br></pre></td></tr></table></figure>



<p><strong>Server输出</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line">MyMessageDecoder decode 被调用</span><br><span class="line">服务器接收到信息如下</span><br><span class="line">长度=<span class="number">27</span></span><br><span class="line">内容=今天天气冷，吃火锅</span><br><span class="line">服务器接收到消息包数量=<span class="number">1</span></span><br><span class="line">服务端开始回复消息------</span><br><span class="line">MyMessageEncoder encode 方法被调用</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">MyMessageDecoder decode 被调用</span><br><span class="line">服务器接收到信息如下</span><br><span class="line">长度=<span class="number">27</span></span><br><span class="line">内容=今天天气冷，吃火锅</span><br><span class="line">服务器接收到消息包数量=<span class="number">2</span></span><br><span class="line">服务端开始回复消息------</span><br><span class="line">MyMessageEncoder encode 方法被调用</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">MyMessageDecoder decode 被调用</span><br><span class="line">服务器接收到信息如下</span><br><span class="line">长度=<span class="number">27</span></span><br><span class="line">内容=今天天气冷，吃火锅</span><br><span class="line">服务器接收到消息包数量=<span class="number">3</span></span><br><span class="line">服务端开始回复消息------</span><br><span class="line">MyMessageEncoder encode 方法被调用</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">MyMessageDecoder decode 被调用</span><br><span class="line">服务器接收到信息如下</span><br><span class="line">长度=<span class="number">27</span></span><br><span class="line">内容=今天天气冷，吃火锅</span><br><span class="line">服务器接收到消息包数量=<span class="number">4</span></span><br><span class="line">服务端开始回复消息------</span><br><span class="line">MyMessageEncoder encode 方法被调用</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">MyMessageDecoder decode 被调用</span><br><span class="line">服务器接收到信息如下</span><br><span class="line">长度=<span class="number">27</span></span><br><span class="line">内容=今天天气冷，吃火锅</span><br><span class="line">服务器接收到消息包数量=<span class="number">5</span></span><br><span class="line">服务端开始回复消息------</span><br><span class="line">MyMessageEncoder encode 方法被调用</span><br></pre></td></tr></table></figure>



<p>无论运行几次，Server都是分5次接收的，这样就解决了TCP粘包问题。</p>
<h1 id="用-Netty-自己实现简单的RPC"><a href="#用-Netty-自己实现简单的RPC" class="headerlink" title="用 Netty 自己实现简单的RPC"></a>用 Netty 自己实现简单的RPC</h1><h2 id="RPC-基本介绍"><a href="#RPC-基本介绍" class="headerlink" title="RPC 基本介绍"></a>RPC 基本介绍</h2><ol>
<li><code>RPC（Remote Procedure Call）</code>—远程过程调用，是一个计算机通信协议。该协议允许运行于一台计算机的程序调用另一台计算机的子程序，而程序员无需额外地为这个交互作用编程</li>
<li>两个或多个应用程序都分布在不同的服务器上，它们之间的调用都像是本地方法调用一样(如图)</li>
</ol>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0021.png"/>

<p>过程：</p>
<ol>
<li><p>调用者(<code>Caller</code>)，调用远程API(<code>Remote API</code>)</p>
</li>
<li><p>调用远程API会通过一个RPC代理(<code>RpcProxy</code>)</p>
</li>
<li><p>RPC代理再去调用<code>RpcInvoker</code>(这个是PRC的调用者)</p>
</li>
<li><p><code>RpcInvoker</code>通过RPC连接器(<code>RpcConnector</code>)</p>
</li>
<li><p>RPC连接器用两台机器规定好的PRC协议(<code>RpcProtocol</code>)把数据进行编码</p>
</li>
<li><p>接着RPC连接器通过RpcChannel通道发送到对方的PRC接收器(RpcAcceptor)</p>
</li>
<li><p>PRC接收器通过PRC协议进行解码拿到数据</p>
</li>
<li><p>然后将数据传给<code>RpcProcessor</code></p>
</li>
<li><p><code>RpcProcessor</code>再传给<code>RpcInvoker</code></p>
</li>
<li><p><code>RpcInvoker</code>调用<code>Remote API</code></p>
</li>
<li><p>最后推给被调用者(Callee)</p>
</li>
<li><p>常见的 <code>RPC</code> 框架有：比较知名的如阿里的 <code>Dubbo</code>、<code>Google</code> 的 <code>gRPC</code>、<code>Go</code> 语言的  <code>rpcx</code>、<code>Apache</code> 的 <code>thrift</code>，<code>Spring</code> 旗下的 <code>SpringCloud</code>。</p>
</li>
</ol>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0022.png"/>

<h2 id="我们的RPC-调用流程图"><a href="#我们的RPC-调用流程图" class="headerlink" title="我们的RPC 调用流程图"></a>我们的RPC 调用流程图</h2><img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0023.png"/>

<p><strong>RPC 调用流程说明</strong></p>
<ol>
<li>服务消费方（<code>client</code>）以本地调用方式调用服务</li>
<li><code>client stub</code> 接收到调用后负责将方法、参数等封装成能够进行网络传输的消息体</li>
<li><code>client stub</code> 将消息进行编码并发送到服务端</li>
<li><code>server stub</code> 收到消息后进行解码</li>
<li><code>server stub</code> 根据解码结果调用本地的服务</li>
<li>本地服务执行并将结果返回给 <code>server stub</code></li>
<li><code>server stub</code> 将返回导入结果进行编码并发送至消费方</li>
<li><code>client stub</code> 接收到消息并进行解码</li>
<li>服务消费方（<code>client</code>）得到结果</li>
</ol>
<p>小结：<code>RPC</code> 的目标就是将 <code>2 - 8</code> 这些步骤都封装起来，用户无需关心这些细节，可以像调用本地方法一样即可完成远程服务调用</p>
<h2 id="己实现-Dubbo-RPC（基于-Netty）"><a href="#己实现-Dubbo-RPC（基于-Netty）" class="headerlink" title="己实现 Dubbo RPC（基于 Netty）"></a>己实现 Dubbo RPC（基于 Netty）</h2><h3 id="需求说明"><a href="#需求说明" class="headerlink" title="需求说明"></a>需求说明</h3><ol>
<li><code>Dubbo</code> 底层使用了 <code>Netty</code> 作为网络通讯框架，要求用 <code>Netty</code> 实现一个简单的 <code>RPC</code> 框架</li>
<li>模仿 <code>Dubbo</code>，消费者和提供者约定接口和协议，消费者远程调用提供者的服务，提供者返回一个字符串，消费者打印提供者返回的数据。底层网络通信使用 <code>Netty 4.1.20</code></li>
</ol>
<h3 id="设计说明"><a href="#设计说明" class="headerlink" title="设计说明"></a>设计说明</h3><ol>
<li>创建一个接口，定义抽象方法。用于消费者和提供者之间的约定。</li>
<li>创建一个提供者，该类需要监听消费者的请求，并按照约定返回数据。</li>
<li>创建一个消费者，该类需要透明的调用自己不存在的方法，内部需要使用 <code>Netty</code> 请求提供者返回数据</li>
<li>开发的分析图</li>
</ol>
<img src="https://npm.elemecdn.com/youthlql@1.0.0/netty/introduction/chapter_003/0024.png" />





<h3 id="代码"><a href="#代码" class="headerlink" title="代码"></a>代码</h3><h4 id="封装的RPC"><a href="#封装的RPC" class="headerlink" title="封装的RPC"></a>封装的RPC</h4><blockquote>
<p>可以把这块代码理解成封装的dubbo</p>
</blockquote>
<h5 id="NettyServer-2"><a href="#NettyServer-2" class="headerlink" title="NettyServer"></a>NettyServer</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.dubborpc.netty;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> io.netty.bootstrap.ServerBootstrap;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelFuture;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelInitializer;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelPipeline;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.EventLoopGroup;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.nio.NioEventLoopGroup;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.socket.SocketChannel;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.socket.nio.NioServerSocketChannel;</span><br><span class="line"><span class="keyword">import</span> io.netty.handler.codec.string.StringDecoder;</span><br><span class="line"><span class="keyword">import</span> io.netty.handler.codec.string.StringEncoder;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">NettyServer</span> &#123;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">startServer</span><span class="params">(String hostName, <span class="type">int</span> port)</span> &#123;</span><br><span class="line">        startServer0(hostName,port);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//编写一个方法，完成对NettyServer的初始化和启动</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">startServer0</span><span class="params">(String hostname, <span class="type">int</span> port)</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="type">EventLoopGroup</span> <span class="variable">bossGroup</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">NioEventLoopGroup</span>(<span class="number">1</span>);</span><br><span class="line">        <span class="type">EventLoopGroup</span> <span class="variable">workerGroup</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">NioEventLoopGroup</span>();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line"></span><br><span class="line">            <span class="type">ServerBootstrap</span> <span class="variable">serverBootstrap</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ServerBootstrap</span>();</span><br><span class="line"></span><br><span class="line">            serverBootstrap.group(bossGroup,workerGroup)</span><br><span class="line">                    .channel(NioServerSocketChannel.class)</span><br><span class="line">                    .childHandler(<span class="keyword">new</span> <span class="title class_">ChannelInitializer</span>&lt;SocketChannel&gt;() &#123;</span><br><span class="line">                                      <span class="meta">@Override</span></span><br><span class="line">                                      <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">initChannel</span><span class="params">(SocketChannel ch)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">                                          <span class="type">ChannelPipeline</span> <span class="variable">pipeline</span> <span class="operator">=</span> ch.pipeline();</span><br><span class="line">                                          pipeline.addLast(<span class="keyword">new</span> <span class="title class_">StringDecoder</span>());</span><br><span class="line">                                          pipeline.addLast(<span class="keyword">new</span> <span class="title class_">StringEncoder</span>());</span><br><span class="line">                                          pipeline.addLast(<span class="keyword">new</span> <span class="title class_">NettyServerHandler</span>()); <span class="comment">//业务处理器</span></span><br><span class="line"></span><br><span class="line">                                      &#125;</span><br><span class="line">                                  &#125;</span><br><span class="line"></span><br><span class="line">                    );</span><br><span class="line"></span><br><span class="line">            <span class="type">ChannelFuture</span> <span class="variable">channelFuture</span> <span class="operator">=</span> serverBootstrap.bind(hostname, port).sync();</span><br><span class="line">            System.out.println(<span class="string">&quot;服务提供方开始提供服务~~&quot;</span>);</span><br><span class="line">            channelFuture.channel().closeFuture().sync();</span><br><span class="line"></span><br><span class="line">        &#125;<span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">finally</span> &#123;</span><br><span class="line">            bossGroup.shutdownGracefully();</span><br><span class="line">            workerGroup.shutdownGracefully();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h5 id="NettyServerHandler-2"><a href="#NettyServerHandler-2" class="headerlink" title="NettyServerHandler"></a>NettyServerHandler</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.dubborpc.netty;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.atguigu.netty.dubborpc.customer.ClientBootstrap;</span><br><span class="line"><span class="keyword">import</span> com.atguigu.netty.dubborpc.provider.HelloServiceImpl;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelHandlerContext;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelInboundHandlerAdapter;</span><br><span class="line"></span><br><span class="line"><span class="comment">//服务器这边handler比较简单</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">NettyServerHandler</span> <span class="keyword">extends</span> <span class="title class_">ChannelInboundHandlerAdapter</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">channelRead</span><span class="params">(ChannelHandlerContext ctx, Object msg)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;---服务端开始收到来自客户单的消息---&quot;</span>);</span><br><span class="line">        <span class="comment">//获取客户端发送的消息，并调用服务</span></span><br><span class="line">        System.out.println(<span class="string">&quot;原始消息：&quot;</span> + msg);</span><br><span class="line"></span><br><span class="line">        <span class="comment">/*</span></span><br><span class="line"><span class="comment">         1.客户端在调用服务器的api 时，我们需要定义一个协议，比如我们要求 每次发消息是都</span></span><br><span class="line"><span class="comment">         必须以某个字符串开头 &quot;HelloService#hello#你好&quot;</span></span><br><span class="line"><span class="comment">         2.Dubbo注册在Zookeeper里时，这种就是类的全路径字符串，你用IDEA的zookeeper插件</span></span><br><span class="line"><span class="comment">         就可以清楚地看到</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        <span class="keyword">if</span>(msg.toString().startsWith(ClientBootstrap.providerName)) &#123;</span><br><span class="line"></span><br><span class="line">            <span class="type">String</span> <span class="variable">result</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HelloServiceImpl</span>().hello(msg.toString().substring(msg.toString().lastIndexOf(<span class="string">&quot;#&quot;</span>) + <span class="number">1</span>));</span><br><span class="line">            ctx.writeAndFlush(result);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">exceptionCaught</span><span class="params">(ChannelHandlerContext ctx, Throwable cause)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        ctx.close();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h5 id="NettyClientHandler-2"><a href="#NettyClientHandler-2" class="headerlink" title="NettyClientHandler"></a>NettyClientHandler</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.dubborpc.netty;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelHandlerContext;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelInboundHandlerAdapter;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.concurrent.Callable;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">NettyClientHandler</span> <span class="keyword">extends</span> <span class="title class_">ChannelInboundHandlerAdapter</span> <span class="keyword">implements</span> <span class="title class_">Callable</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> ChannelHandlerContext context;<span class="comment">//上下文</span></span><br><span class="line">    <span class="keyword">private</span> String result; <span class="comment">//返回的结果</span></span><br><span class="line">    <span class="keyword">private</span> String para; <span class="comment">//客户端调用方法时，传入的参数</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">//与服务器的连接创建后，就会被调用, 这个方法是第一个被调用(1)</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">channelActive</span><span class="params">(ChannelHandlerContext ctx)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot; channelActive 被调用  &quot;</span>);</span><br><span class="line">        context = ctx; <span class="comment">//因为我们在其它方法会使用到 ctx</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//收到服务器的数据后，调用方法 (4)</span></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title function_">channelRead</span><span class="params">(ChannelHandlerContext ctx, Object msg)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot; channelRead 被调用  &quot;</span>);</span><br><span class="line">        result = msg.toString();</span><br><span class="line">        notify(); <span class="comment">//唤醒等待的线程</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">exceptionCaught</span><span class="params">(ChannelHandlerContext ctx, Throwable cause)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        ctx.close();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//被代理对象调用, 发送数据给服务器，-&gt; wait -&gt; 等待被唤醒(channelRead) -&gt; 返回结果 (3)-》5</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">synchronized</span> Object <span class="title function_">call</span><span class="params">()</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot; call1 被调用  &quot;</span>);</span><br><span class="line">        context.writeAndFlush(para);</span><br><span class="line">        <span class="comment">//进行wait</span></span><br><span class="line">        wait(); <span class="comment">//等待channelRead 方法获取到服务器的结果后，唤醒</span></span><br><span class="line">        System.out.println(<span class="string">&quot; call2 被调用  &quot;</span>);</span><br><span class="line">        <span class="keyword">return</span>  result; <span class="comment">//服务方返回的结果</span></span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//(2)</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">setPara</span><span class="params">(String para)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot; setPara  &quot;</span>);</span><br><span class="line">        <span class="built_in">this</span>.para = para;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h5 id="NettyClient-2"><a href="#NettyClient-2" class="headerlink" title="NettyClient"></a>NettyClient</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.dubborpc.netty;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> io.netty.bootstrap.Bootstrap;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelInitializer;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelOption;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.ChannelPipeline;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.nio.NioEventLoopGroup;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.socket.SocketChannel;</span><br><span class="line"><span class="keyword">import</span> io.netty.channel.socket.nio.NioSocketChannel;</span><br><span class="line"><span class="keyword">import</span> io.netty.handler.codec.string.StringDecoder;</span><br><span class="line"><span class="keyword">import</span> io.netty.handler.codec.string.StringEncoder;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Proxy;</span><br><span class="line"><span class="keyword">import</span> java.util.concurrent.Executor;</span><br><span class="line"><span class="keyword">import</span> java.util.concurrent.ExecutorService;</span><br><span class="line"><span class="keyword">import</span> java.util.concurrent.Executors;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">NettyClient</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//创建线程池</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="type">ExecutorService</span> <span class="variable">executor</span> <span class="operator">=</span> Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> NettyClientHandler client;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//编写方法使用代理模式，获取一个代理对象</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Object <span class="title function_">getBean</span><span class="params">(<span class="keyword">final</span> Class&lt;?&gt; serivceClass, <span class="keyword">final</span> String providerName)</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),</span><br><span class="line">                <span class="keyword">new</span> <span class="title class_">Class</span>&lt;?&gt;[]&#123;serivceClass&#125;, (proxy, method, args) -&gt; &#123;</span><br><span class="line"></span><br><span class="line">                    System.out.println(<span class="string">&quot;(proxy, method, args) 进入....&quot;</span> + (++count) + <span class="string">&quot; 次&quot;</span>);</span><br><span class="line">                    <span class="comment">//&#123;&#125;  部分的代码，客户端每调用一次 hello, 就会进入到该代码</span></span><br><span class="line">                    <span class="keyword">if</span> (client == <span class="literal">null</span>) &#123;</span><br><span class="line">                        initClient();</span><br><span class="line">                    &#125;</span><br><span class="line"></span><br><span class="line">                    <span class="comment">//设置要发给服务器端的信息</span></span><br><span class="line">                    <span class="comment">//providerName：协议头，args[0]：就是客户端要发送给服务端的数据</span></span><br><span class="line">                    client.setPara(providerName + args[<span class="number">0</span>]);</span><br><span class="line"></span><br><span class="line">                    <span class="comment">//</span></span><br><span class="line">                    <span class="keyword">return</span> executor.submit(client).get();</span><br><span class="line"></span><br><span class="line">                &#125;);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//初始化客户端</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">initClient</span><span class="params">()</span> &#123;</span><br><span class="line">        client = <span class="keyword">new</span> <span class="title class_">NettyClientHandler</span>();</span><br><span class="line">        <span class="comment">//创建EventLoopGroup</span></span><br><span class="line">        <span class="type">NioEventLoopGroup</span> <span class="variable">group</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">NioEventLoopGroup</span>();</span><br><span class="line">        <span class="type">Bootstrap</span> <span class="variable">bootstrap</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Bootstrap</span>();</span><br><span class="line">        bootstrap.group(group)</span><br><span class="line">                .channel(NioSocketChannel.class)</span><br><span class="line">                .option(ChannelOption.TCP_NODELAY, <span class="literal">true</span>)</span><br><span class="line">                .handler(</span><br><span class="line">                        <span class="keyword">new</span> <span class="title class_">ChannelInitializer</span>&lt;SocketChannel&gt;() &#123;</span><br><span class="line">                            <span class="meta">@Override</span></span><br><span class="line">                            <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">initChannel</span><span class="params">(SocketChannel ch)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">                                <span class="type">ChannelPipeline</span> <span class="variable">pipeline</span> <span class="operator">=</span> ch.pipeline();</span><br><span class="line">                                pipeline.addLast(<span class="keyword">new</span> <span class="title class_">StringDecoder</span>());</span><br><span class="line">                                pipeline.addLast(<span class="keyword">new</span> <span class="title class_">StringEncoder</span>());</span><br><span class="line">                                pipeline.addLast(client);</span><br><span class="line">                            &#125;</span><br><span class="line">                        &#125;</span><br><span class="line">                );</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            bootstrap.connect(<span class="string">&quot;127.0.0.1&quot;</span>, <span class="number">7000</span>).sync();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>



<h4 id="接口"><a href="#接口" class="headerlink" title="接口"></a>接口</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.dubborpc.publicinterface;</span><br><span class="line"></span><br><span class="line"><span class="comment">//这个是接口，是服务提供方和 服务消费方都需要</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">HelloService</span> &#123;</span><br><span class="line"></span><br><span class="line">    String <span class="title function_">hello</span><span class="params">(String mes)</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>





<h4 id="服务端-provider"><a href="#服务端-provider" class="headerlink" title="服务端(provider)"></a>服务端(provider)</h4><h5 id="HelloServiceImpl"><a href="#HelloServiceImpl" class="headerlink" title="HelloServiceImpl"></a>HelloServiceImpl</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.dubborpc.provider;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.atguigu.netty.dubborpc.publicinterface.HelloService;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HelloServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">HelloService</span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="comment">//当有消费方调用该方法时， 就返回一个结果</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">hello</span><span class="params">(String mes)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;收到客户端消息=&quot;</span> + mes);</span><br><span class="line">        System.out.println();</span><br><span class="line">        <span class="comment">//根据mes 返回不同的结果</span></span><br><span class="line">        <span class="keyword">if</span>(mes != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="string">&quot;你好客户端, 我已经收到你的消息。消息为：[&quot;</span> + mes + <span class="string">&quot;] ，第&quot;</span> + (++count) + <span class="string">&quot; 次 \n&quot;</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="string">&quot;你好客户端, 我已经收到你的消息 &quot;</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>



<h5 id="ServerBootstrap"><a href="#ServerBootstrap" class="headerlink" title="ServerBootstrap"></a>ServerBootstrap</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.dubborpc.provider;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.atguigu.netty.dubborpc.netty.NettyServer;</span><br><span class="line"></span><br><span class="line"><span class="comment">//ServerBootstrap 会启动一个服务提供者，就是 NettyServer</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ServerBootstrap</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//代码代填..</span></span><br><span class="line">        NettyServer.startServer(<span class="string">&quot;127.0.0.1&quot;</span>, <span class="number">7000</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>



<h4 id="客户端-消费者"><a href="#客户端-消费者" class="headerlink" title="客户端(消费者)"></a>客户端(消费者)</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.atguigu.netty.dubborpc.customer;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.atguigu.netty.dubborpc.netty.NettyClient;</span><br><span class="line"><span class="keyword">import</span> com.atguigu.netty.dubborpc.publicinterface.HelloService;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ClientBootstrap</span> &#123;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">//这里定义协议头</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">providerName</span> <span class="operator">=</span> <span class="string">&quot;HelloService#hello#&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span>  Exception&#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//创建一个消费者</span></span><br><span class="line">        <span class="type">NettyClient</span> <span class="variable">customer</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">NettyClient</span>();</span><br><span class="line"></span><br><span class="line">        <span class="comment">//创建代理对象</span></span><br><span class="line">        <span class="type">HelloService</span> <span class="variable">service</span> <span class="operator">=</span> (HelloService) customer.getBean(HelloService.class, providerName);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> (;; ) &#123;</span><br><span class="line">            Thread.sleep(<span class="number">2</span> * <span class="number">1000</span>);</span><br><span class="line">            <span class="comment">//通过代理对象调用服务提供者的方法(服务)</span></span><br><span class="line">            <span class="type">String</span> <span class="variable">res</span> <span class="operator">=</span> service.hello(<span class="string">&quot;你好 dubbo~&quot;</span>);</span><br><span class="line">            System.out.println(<span class="string">&quot;调用的结果 res= &quot;</span> + res);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>





<h3 id="调用过程"><a href="#调用过程" class="headerlink" title="调用过程"></a>调用过程</h3><ol>
<li><code>ClientBootstrap#main</code>发起调用</li>
<li>走到下面这一行代码后</li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">HelloService</span> <span class="variable">service</span> <span class="operator">=</span> (HelloService) customer.getBean(HelloService.class, providerName);</span><br></pre></td></tr></table></figure>

<ol start="3">
<li><p>调用<code>NettyClient#getBean</code>，在此方法里与服务端建立链接。</p>
</li>
<li><p>于是就执行<code>NettyClientHandler#channelActive</code></p>
</li>
<li><p>接着回到<code>NettyClient#getBean</code>调用<code>NettyClientHandler#setPara</code>，调用完之后再回到<code>NettyClient#getBean</code>，用线程池提交任务</p>
</li>
<li><p>因为用线程池提交了任务，就准备执行<code>NettyClientHandler#call</code>线程任务</p>
</li>
<li><p>在<code>NettyClientHandler#call</code>中发送数据给服务提供者</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">context.writeAndFlush(para);</span><br></pre></td></tr></table></figure>

<p>由于还没收到服务提供者的数据结果，所以wait住</p>
</li>
<li><p>来到了服务提供者这边，从Socket通道中收到了数据，所以执行<code>NettyServerHandler#channelRead</code>，然后因为此方法中执行了</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">String</span> <span class="variable">result</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HelloServiceImpl</span>().hello(msg.toString().substring(msg.toString().lastIndexOf(<span class="string">&quot;#&quot;</span>) + <span class="number">1</span>));</span><br></pre></td></tr></table></figure>
</li>
<li><p>就去<code>HelloServiceImpl#hello</code>中执行业务逻辑，返回数据给<code>NettyServerHandler#channelRead</code>，<code>NettyServerHandler#channelRead</code>再把数据发给客户端</p>
</li>
<li><p><code>NettyClientHandler#channelRead</code>收到服务提供者发来的数据，唤醒之前wait的线程</p>
</li>
<li><p>所以之前wait的线程从<code>NettyClientHandler#call</code>苏醒，返回result给<code>NettyClient#getBean</code></p>
</li>
<li><p><code>NettyClient#getBean</code>get()到数据，<code>ClientBootstrap#main</code>中的此函数调用返回，得到服务端提供的数据。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">String</span> <span class="variable">res</span> <span class="operator">=</span> service.hello(<span class="string">&quot;你好 dubbo~&quot;</span>);</span><br></pre></td></tr></table></figure></li>
</ol>
<p>13.至此，一次RPC调用结束。</p>
<h3 id="效果-3"><a href="#效果-3" class="headerlink" title="效果"></a>效果</h3><p><strong>ClientBootstrap打印</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line">(proxy, method, args) 进入...<span class="number">.1</span> 次</span><br><span class="line"> setPara  </span><br><span class="line"> channelActive 被调用  </span><br><span class="line"> call1 被调用  </span><br><span class="line"> channelRead 被调用  </span><br><span class="line"> call2 被调用  </span><br><span class="line">调用的结果 res= 你好客户端, 我已经收到你的消息。消息为：[你好 dubbo~] ，第<span class="number">1</span> 次 </span><br><span class="line"></span><br><span class="line">(proxy, method, args) 进入...<span class="number">.2</span> 次</span><br><span class="line"> setPara  </span><br><span class="line"> call1 被调用  </span><br><span class="line"> channelRead 被调用  </span><br><span class="line"> call2 被调用  </span><br><span class="line">调用的结果 res= 你好客户端, 我已经收到你的消息。消息为：[你好 dubbo~] ，第<span class="number">2</span> 次 </span><br><span class="line"></span><br><span class="line">(proxy, method, args) 进入...<span class="number">.3</span> 次</span><br><span class="line"> setPara  </span><br><span class="line"> call1 被调用  </span><br><span class="line"> channelRead 被调用  </span><br><span class="line"> call2 被调用  </span><br><span class="line">调用的结果 res= 你好客户端, 我已经收到你的消息。消息为：[你好 dubbo~] ，第<span class="number">3</span> 次 </span><br><span class="line"></span><br><span class="line">(proxy, method, args) 进入...<span class="number">.4</span> 次</span><br><span class="line"> setPara  </span><br><span class="line"> call1 被调用  </span><br><span class="line"> channelRead 被调用  </span><br><span class="line"> call2 被调用  </span><br><span class="line">调用的结果 res= 你好客户端, 我已经收到你的消息。消息为：[你好 dubbo~] ，第<span class="number">4</span> 次 </span><br><span class="line"></span><br><span class="line">(proxy, method, args) 进入...<span class="number">.5</span> 次</span><br><span class="line"> setPara  </span><br><span class="line"> call1 被调用  </span><br><span class="line"> channelRead 被调用  </span><br><span class="line"> call2 被调用  </span><br><span class="line">调用的结果 res= 你好客户端, 我已经收到你的消息。消息为：[你好 dubbo~] ，第<span class="number">5</span> 次 </span><br></pre></td></tr></table></figure>





<p><strong>ServerBootstrap打印</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">服务提供方开始提供服务~~</span><br><span class="line">---服务端开始收到来自客户单的消息---</span><br><span class="line">原始消息：HelloService#hello#你好 dubbo~</span><br><span class="line">收到客户端消息=你好 dubbo~</span><br><span class="line"></span><br><span class="line">---服务端开始收到来自客户单的消息---</span><br><span class="line">原始消息：HelloService#hello#你好 dubbo~</span><br><span class="line">收到客户端消息=你好 dubbo~</span><br><span class="line"></span><br><span class="line">---服务端开始收到来自客户单的消息---</span><br><span class="line">原始消息：HelloService#hello#你好 dubbo~</span><br><span class="line">收到客户端消息=你好 dubbo~</span><br><span class="line"></span><br><span class="line">---服务端开始收到来自客户单的消息---</span><br><span class="line">原始消息：HelloService#hello#你好 dubbo~</span><br><span class="line">收到客户端消息=你好 dubbo~</span><br><span class="line"></span><br><span class="line">---服务端开始收到来自客户单的消息---</span><br><span class="line">原始消息：HelloService#hello#你好 dubbo~</span><br><span class="line">收到客户端消息=你好 dubbo~</span><br><span class="line"></span><br></pre></td></tr></table></figure>











</article><div class="post-copyright"><div class="post-copyright__author"><span class="post-copyright-meta">文章作者: </span><span class="post-copyright-info"><a href="http://example.com">Alix</a></span></div><div class="post-copyright__type"><span class="post-copyright-meta">文章链接: </span><span class="post-copyright-info"><a href="http://example.com/2022/04/21/netty/Netty%E5%85%A5%E9%97%A8-%E7%AC%AC%E4%B8%89%E8%AF%9D/">http://example.com/2022/04/21/netty/Netty%E5%85%A5%E9%97%A8-%E7%AC%AC%E4%B8%89%E8%AF%9D/</a></span></div><div class="post-copyright__notice"><span class="post-copyright-meta">版权声明: </span><span class="post-copyright-info">本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">CC BY-NC-SA 4.0</a> 许可协议。转载请注明来自 <a href="http://example.com" target="_blank">石银的小酒屋</a>！</span></div></div><div class="tag_share"><div class="post-meta__tag-list"><a class="post-meta__tags" href="/tags/Netty/">Netty</a></div><div class="post_share"><div class="social-share" data-image="https://npm.elemecdn.com/lql_static@latest/logo/netty_logo.jpg" data-sites="facebook,twitter,wechat,weibo,qq"></div><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/butterfly-extsrc/sharejs/dist/css/share.min.css" media="print" onload="this.media='all'"><script src="https://cdn.jsdelivr.net/npm/butterfly-extsrc/sharejs/dist/js/social-share.min.js" defer></script></div></div><nav class="pagination-post" id="pagination"><div class="prev-post pull-left"><a href="/2023/04/20/%E9%94%81%E6%9C%BA%E5%88%B6/"><img class="prev-cover" src="/assetse/y13.jpg" onerror="onerror=null;src='/img/404.jpg'" alt="cover of previous post"><div class="pagination-info"><div class="label">上一篇</div><div class="prev_info">数据库并发锁机制</div></div></a></div><div class="next-post pull-right"><a href="/2022/04/15/netty/Netty%E5%85%A5%E9%97%A8-%E7%AC%AC%E4%BA%8C%E8%AF%9D/"><img class="next-cover" src="https://npm.elemecdn.com/lql_static@latest/logo/netty_logo.jpg" onerror="onerror=null;src='/img/404.jpg'" alt="cover of next post"><div class="pagination-info"><div class="label">下一篇</div><div class="next_info">Netty入门-第二话</div></div></a></div></nav><div class="relatedPosts"><div class="headline"><i class="fas fa-thumbs-up fa-fw"></i><span>相关推荐</span></div><div class="relatedPosts-list"><div><a href="/2022/04/08/netty/Netty%E5%85%A5%E9%97%A8-%E7%AC%AC%E4%B8%80%E8%AF%9D/" title="Netty入门-第一话"><img class="cover" src="https://npm.elemecdn.com/lql_static@latest/logo/netty_logo.jpg" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2022-04-08</div><div class="title">Netty入门-第一话</div></div></a></div><div><a href="/2022/04/15/netty/Netty%E5%85%A5%E9%97%A8-%E7%AC%AC%E4%BA%8C%E8%AF%9D/" title="Netty入门-第二话"><img class="cover" src="https://npm.elemecdn.com/lql_static@latest/logo/netty_logo.jpg" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2022-04-15</div><div class="title">Netty入门-第二话</div></div></a></div></div></div><hr/><div id="post-comment"><div class="comment-head"><div class="comment-headline"><i class="fas fa-comments fa-fw"></i><span> 评论</span></div><div id="comment-switch"><span class="first-comment">Valine</span><span class="switch-btn"></span><span class="second-comment">Disqus</span></div></div><div class="comment-wrap"><div><div class="vcomment" id="vcomment"></div></div><div><div id="disqus_thread"></div></div></div></div></div><div class="aside-content" id="aside-content"><div class="card-widget card-info"><div class="is-center"><div class="avatar-img"><img src="/assetse/%E5%A4%B4%E5%83%8F.jpg" onerror="this.onerror=null;this.src='/img/friend_404.gif'" alt="avatar"/></div><div class="author-info__name">Alix</div><div class="author-info__description">归途也还可爱</div></div><div class="card-info-data site-data is-center"><a href="/archives/"><div class="headline">文章</div><div class="length-num">23</div></a><a href="/tags/"><div class="headline">标签</div><div class="length-num">9</div></a><a href="/categories/"><div class="headline">分类</div><div class="length-num">7</div></a></div><a id="card-info-btn" target="_blank" rel="noopener" href="https://github.com/923584731"><i class="fab fa-github"></i><span>Follow Me</span></a><div class="card-info-social-icons is-center"><a class="social-icon" href="https://github.com/923584731" target="_blank" title="Github"><i class="fab fa-github"></i></a><a class="social-icon" href="mailto:923584731@qq.com" target="_blank" title="Email"><i class="fas fa-envelope"></i></a><a class="social-icon" href="/atom.xml" target="_blank" title="Rss"><i class="fas fa-rss"></i></a></div></div><div class="card-widget card-announcement"><div class="item-headline"><i class="fas fa-bullhorn fa-shake"></i><span>公告</span></div><div class="announcement_content">少年郎的博客网站</div></div><div class="sticky_layout"><div class="card-widget" id="card-toc"><div class="item-headline"><i class="fas fa-stream"></i><span>目录</span><span class="toc-percentage"></span></div><div class="toc-content"><ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#Google-Protobuf"><span class="toc-number">1.</span> <span class="toc-text">Google Protobuf</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%BC%96%E7%A0%81%E5%92%8C%E8%A7%A3%E7%A0%81%E7%9A%84%E5%9F%BA%E6%9C%AC%E4%BB%8B%E7%BB%8D"><span class="toc-number">1.1.</span> <span class="toc-text">编码和解码的基本介绍</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Netty-%E6%9C%AC%E8%BA%AB%E7%9A%84%E7%BC%96%E7%A0%81%E8%A7%A3%E7%A0%81%E7%9A%84%E6%9C%BA%E5%88%B6%E5%92%8C%E9%97%AE%E9%A2%98%E5%88%86%E6%9E%90"><span class="toc-number">1.2.</span> <span class="toc-text">Netty 本身的编码解码的机制和问题分析</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Protobuf"><span class="toc-number">1.3.</span> <span class="toc-text">Protobuf</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Protobuf-%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8%E5%AE%9E%E4%BE%8B"><span class="toc-number">1.4.</span> <span class="toc-text">Protobuf 快速入门实例</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#Student-proto"><span class="toc-number">1.4.1.</span> <span class="toc-text">Student.proto</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#NettyServer"><span class="toc-number">1.4.2.</span> <span class="toc-text">NettyServer</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#NettyServerHandler"><span class="toc-number">1.4.3.</span> <span class="toc-text">NettyServerHandler</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#NettyClient"><span class="toc-number">1.4.4.</span> <span class="toc-text">NettyClient</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#NettyClientHandler"><span class="toc-number">1.4.5.</span> <span class="toc-text">NettyClientHandler</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Protobuf-%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8%E5%AE%9E%E4%BE%8B-2"><span class="toc-number">1.5.</span> <span class="toc-text">Protobuf 快速入门实例 2</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#proto"><span class="toc-number">1.5.1.</span> <span class="toc-text">proto</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#NettyServer-1"><span class="toc-number">1.5.2.</span> <span class="toc-text">NettyServer</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#NettyServerHandler-1"><span class="toc-number">1.5.3.</span> <span class="toc-text">NettyServerHandler</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#NettyClient-1"><span class="toc-number">1.5.4.</span> <span class="toc-text">NettyClient</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#NettyClientHandler-1"><span class="toc-number">1.5.5.</span> <span class="toc-text">NettyClientHandler</span></a></li></ol></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#Netty-%E7%BC%96%E8%A7%A3%E7%A0%81%E5%99%A8%E5%92%8C-Handler-%E8%B0%83%E7%94%A8%E6%9C%BA%E5%88%B6"><span class="toc-number">2.</span> <span class="toc-text">Netty 编解码器和 Handler 调用机制</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%9F%BA%E6%9C%AC%E8%AF%B4%E6%98%8E"><span class="toc-number">2.1.</span> <span class="toc-text">基本说明</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%BC%96%E7%A0%81%E8%A7%A3%E7%A0%81%E5%99%A8"><span class="toc-number">2.2.</span> <span class="toc-text">编码解码器</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E8%A7%A3%E7%A0%81%E5%99%A8-ByteToMessageDecoder"><span class="toc-number">2.3.</span> <span class="toc-text">解码器 - ByteToMessageDecoder</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Netty%E7%9A%84handler%E9%93%BE%E7%9A%84%E8%B0%83%E7%94%A8%E6%9C%BA%E5%88%B6"><span class="toc-number">2.4.</span> <span class="toc-text">Netty的handler链的调用机制</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#MyServer"><span class="toc-number">2.4.1.</span> <span class="toc-text">MyServer</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#MyServerInitializer"><span class="toc-number">2.4.2.</span> <span class="toc-text">MyServerInitializer</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#MyServerHandler"><span class="toc-number">2.4.3.</span> <span class="toc-text">MyServerHandler</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#MyClient"><span class="toc-number">2.4.4.</span> <span class="toc-text">MyClient</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#MyClientInitializer"><span class="toc-number">2.4.5.</span> <span class="toc-text">MyClientInitializer</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#MyClientHandler"><span class="toc-number">2.4.6.</span> <span class="toc-text">MyClientHandler</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#MyByteToLongDecoder"><span class="toc-number">2.4.7.</span> <span class="toc-text">MyByteToLongDecoder</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#MyLongToByteEncoder"><span class="toc-number">2.4.8.</span> <span class="toc-text">MyLongToByteEncoder</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%95%88%E6%9E%9C"><span class="toc-number">2.4.9.</span> <span class="toc-text">效果</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%87%BA%E7%AB%99%E5%85%A5%E7%AB%99"><span class="toc-number">2.4.10.</span> <span class="toc-text">出站入站</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#ByteToMessageDecoder%E7%9A%84%E5%B0%8F%E7%BB%86%E8%8A%82"><span class="toc-number">2.5.</span> <span class="toc-text">ByteToMessageDecoder的小细节</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E8%A7%A3%E7%A0%81%E5%99%A8-ReplayingDecoder"><span class="toc-number">2.6.</span> <span class="toc-text">解码器 - ReplayingDecoder</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%85%B6%E5%AE%83%E7%BC%96%E8%A7%A3%E7%A0%81%E5%99%A8"><span class="toc-number">2.7.</span> <span class="toc-text">其它编解码器</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Log4j-%E6%95%B4%E5%90%88%E5%88%B0-Netty"><span class="toc-number">2.8.</span> <span class="toc-text">Log4j 整合到 Netty</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#TCP-%E7%B2%98%E5%8C%85%E5%92%8C%E6%8B%86%E5%8C%85%E5%8F%8A%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88"><span class="toc-number">3.</span> <span class="toc-text">TCP 粘包和拆包及解决方案</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#TCP-%E7%B2%98%E5%8C%85%E5%92%8C%E6%8B%86%E5%8C%85%E5%9F%BA%E6%9C%AC%E4%BB%8B%E7%BB%8D"><span class="toc-number">3.1.</span> <span class="toc-text">TCP 粘包和拆包基本介绍</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#TCP-%E7%B2%98%E5%8C%85%E5%92%8C%E6%8B%86%E5%8C%85%E7%8E%B0%E8%B1%A1%E5%AE%9E%E4%BE%8B"><span class="toc-number">3.2.</span> <span class="toc-text">TCP 粘包和拆包现象实例</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#MyServer-1"><span class="toc-number">3.2.1.</span> <span class="toc-text">MyServer</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#MyServerInitializer-1"><span class="toc-number">3.2.2.</span> <span class="toc-text">MyServerInitializer</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#MyServerHandler-1"><span class="toc-number">3.2.3.</span> <span class="toc-text">MyServerHandler</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#MyClient-1"><span class="toc-number">3.2.4.</span> <span class="toc-text">MyClient</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#MyClientInitializer-1"><span class="toc-number">3.2.5.</span> <span class="toc-text">MyClientInitializer</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#MyClientHandler-1"><span class="toc-number">3.2.6.</span> <span class="toc-text">MyClientHandler</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%95%88%E6%9E%9C-1"><span class="toc-number">3.2.7.</span> <span class="toc-text">效果</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#TCP-%E7%B2%98%E5%8C%85%E5%92%8C%E6%8B%86%E5%8C%85%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88"><span class="toc-number">3.3.</span> <span class="toc-text">TCP 粘包和拆包解决方案</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#MessageProtocol"><span class="toc-number">3.3.1.</span> <span class="toc-text">MessageProtocol</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#MyServer-2"><span class="toc-number">3.3.2.</span> <span class="toc-text">MyServer</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#MyServerInitializer-2"><span class="toc-number">3.3.3.</span> <span class="toc-text">MyServerInitializer</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#MyServerHandler-2"><span class="toc-number">3.3.4.</span> <span class="toc-text">MyServerHandler</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#MyClient-2"><span class="toc-number">3.3.5.</span> <span class="toc-text">MyClient</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#MyClientInitializer-2"><span class="toc-number">3.3.6.</span> <span class="toc-text">MyClientInitializer</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#MyClientHandler-2"><span class="toc-number">3.3.7.</span> <span class="toc-text">MyClientHandler</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#MyMessageDecoder"><span class="toc-number">3.3.8.</span> <span class="toc-text">MyMessageDecoder</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#MyMessageEncoder"><span class="toc-number">3.3.9.</span> <span class="toc-text">MyMessageEncoder</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%95%88%E6%9E%9C-2"><span class="toc-number">3.3.10.</span> <span class="toc-text">效果</span></a></li></ol></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#%E7%94%A8-Netty-%E8%87%AA%E5%B7%B1%E5%AE%9E%E7%8E%B0%E7%AE%80%E5%8D%95%E7%9A%84RPC"><span class="toc-number">4.</span> <span class="toc-text">用 Netty 自己实现简单的RPC</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#RPC-%E5%9F%BA%E6%9C%AC%E4%BB%8B%E7%BB%8D"><span class="toc-number">4.1.</span> <span class="toc-text">RPC 基本介绍</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%88%91%E4%BB%AC%E7%9A%84RPC-%E8%B0%83%E7%94%A8%E6%B5%81%E7%A8%8B%E5%9B%BE"><span class="toc-number">4.2.</span> <span class="toc-text">我们的RPC 调用流程图</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%B7%B1%E5%AE%9E%E7%8E%B0-Dubbo-RPC%EF%BC%88%E5%9F%BA%E4%BA%8E-Netty%EF%BC%89"><span class="toc-number">4.3.</span> <span class="toc-text">己实现 Dubbo RPC（基于 Netty）</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E9%9C%80%E6%B1%82%E8%AF%B4%E6%98%8E"><span class="toc-number">4.3.1.</span> <span class="toc-text">需求说明</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%AE%BE%E8%AE%A1%E8%AF%B4%E6%98%8E"><span class="toc-number">4.3.2.</span> <span class="toc-text">设计说明</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BB%A3%E7%A0%81"><span class="toc-number">4.3.3.</span> <span class="toc-text">代码</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%B0%81%E8%A3%85%E7%9A%84RPC"><span class="toc-number">4.3.3.1.</span> <span class="toc-text">封装的RPC</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#NettyServer-2"><span class="toc-number">4.3.3.1.1.</span> <span class="toc-text">NettyServer</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#NettyServerHandler-2"><span class="toc-number">4.3.3.1.2.</span> <span class="toc-text">NettyServerHandler</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#NettyClientHandler-2"><span class="toc-number">4.3.3.1.3.</span> <span class="toc-text">NettyClientHandler</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#NettyClient-2"><span class="toc-number">4.3.3.1.4.</span> <span class="toc-text">NettyClient</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%8E%A5%E5%8F%A3"><span class="toc-number">4.3.3.2.</span> <span class="toc-text">接口</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%9C%8D%E5%8A%A1%E7%AB%AF-provider"><span class="toc-number">4.3.3.3.</span> <span class="toc-text">服务端(provider)</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#HelloServiceImpl"><span class="toc-number">4.3.3.3.1.</span> <span class="toc-text">HelloServiceImpl</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#ServerBootstrap"><span class="toc-number">4.3.3.3.2.</span> <span class="toc-text">ServerBootstrap</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%AE%A2%E6%88%B7%E7%AB%AF-%E6%B6%88%E8%B4%B9%E8%80%85"><span class="toc-number">4.3.3.4.</span> <span class="toc-text">客户端(消费者)</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%B0%83%E7%94%A8%E8%BF%87%E7%A8%8B"><span class="toc-number">4.3.4.</span> <span class="toc-text">调用过程</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%95%88%E6%9E%9C-3"><span class="toc-number">4.3.5.</span> <span class="toc-text">效果</span></a></li></ol></li></ol></li></ol></div></div><div class="card-widget card-recent-post"><div class="item-headline"><i class="fas fa-history"></i><span>最新文章</span></div><div class="aside-list"><div class="aside-list-item"><a class="thumbnail" href="/2023/04/20/%E9%94%81%E6%9C%BA%E5%88%B6/" title="数据库并发锁机制"><img src="/assetse/y13.jpg" onerror="this.onerror=null;this.src='/img/404.jpg'" alt="数据库并发锁机制"/></a><div class="content"><a class="title" href="/2023/04/20/%E9%94%81%E6%9C%BA%E5%88%B6/" title="数据库并发锁机制">数据库并发锁机制</a><time datetime="2023-04-20T06:55:04.000Z" title="发表于 2023-04-20 14:55:04">2023-04-20</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/2022/04/21/netty/Netty%E5%85%A5%E9%97%A8-%E7%AC%AC%E4%B8%89%E8%AF%9D/" title="Netty入门-第三话"><img src="https://npm.elemecdn.com/lql_static@latest/logo/netty_logo.jpg" onerror="this.onerror=null;this.src='/img/404.jpg'" alt="Netty入门-第三话"/></a><div class="content"><a class="title" href="/2022/04/21/netty/Netty%E5%85%A5%E9%97%A8-%E7%AC%AC%E4%B8%89%E8%AF%9D/" title="Netty入门-第三话">Netty入门-第三话</a><time datetime="2022-04-21T09:38:58.000Z" title="发表于 2022-04-21 17:38:58">2022-04-21</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/2022/04/15/netty/Netty%E5%85%A5%E9%97%A8-%E7%AC%AC%E4%BA%8C%E8%AF%9D/" title="Netty入门-第二话"><img src="https://npm.elemecdn.com/lql_static@latest/logo/netty_logo.jpg" onerror="this.onerror=null;this.src='/img/404.jpg'" alt="Netty入门-第二话"/></a><div class="content"><a class="title" href="/2022/04/15/netty/Netty%E5%85%A5%E9%97%A8-%E7%AC%AC%E4%BA%8C%E8%AF%9D/" title="Netty入门-第二话">Netty入门-第二话</a><time datetime="2022-04-15T06:21:58.000Z" title="发表于 2022-04-15 14:21:58">2022-04-15</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/2022/04/08/netty/Netty%E5%85%A5%E9%97%A8-%E7%AC%AC%E4%B8%80%E8%AF%9D/" title="Netty入门-第一话"><img src="https://npm.elemecdn.com/lql_static@latest/logo/netty_logo.jpg" onerror="this.onerror=null;this.src='/img/404.jpg'" alt="Netty入门-第一话"/></a><div class="content"><a class="title" href="/2022/04/08/netty/Netty%E5%85%A5%E9%97%A8-%E7%AC%AC%E4%B8%80%E8%AF%9D/" title="Netty入门-第一话">Netty入门-第一话</a><time datetime="2022-04-08T06:21:58.000Z" title="发表于 2022-04-08 14:21:58">2022-04-08</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/2021/11/19/JVM/JVM%E7%B3%BB%E5%88%97-%E7%AC%AC12%E7%AB%A0-%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8/" title="JVM系列-第12章-垃圾回收器"><img src="https://npm.elemecdn.com/lql_static@latest/logo/jvm.png" onerror="this.onerror=null;this.src='/img/404.jpg'" alt="JVM系列-第12章-垃圾回收器"/></a><div class="content"><a class="title" href="/2021/11/19/JVM/JVM%E7%B3%BB%E5%88%97-%E7%AC%AC12%E7%AB%A0-%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8/" title="JVM系列-第12章-垃圾回收器">JVM系列-第12章-垃圾回收器</a><time datetime="2021-11-19T10:33:24.000Z" title="发表于 2021-11-19 18:33:24">2021-11-19</time></div></div></div></div></div></div></main><footer id="footer"><div id="footer-wrap"><div class="copyright">&copy;2020 - 2023 By Alix</div><div class="framework-info"><span>框架 </span><a target="_blank" rel="noopener" href="https://hexo.io">Hexo</a><span class="footer-separator">|</span><span>主题 </span><a target="_blank" rel="noopener" href="https://github.com/jerryc127/hexo-theme-butterfly">Butterfly</a></div></div></footer></div><div id="rightside"><div id="rightside-config-hide"><button id="readmode" type="button" title="阅读模式"><i class="fas fa-book-open"></i></button><button id="darkmode" type="button" title="浅色和深色模式转换"><i class="fas fa-adjust"></i></button><button id="hide-aside-btn" type="button" title="单栏和双栏切换"><i class="fas fa-arrows-alt-h"></i></button></div><div id="rightside-config-show"><button id="rightside_config" type="button" title="设置"><i class="fas fa-cog fa-spin"></i></button><button class="close" id="mobile-toc-button" type="button" title="目录"><i class="fas fa-list-ul"></i></button><a id="to_comment" href="#post-comment" title="直达评论"><i class="fas fa-comments"></i></a><button id="go-up" type="button" title="回到顶部"><i class="fas fa-arrow-up"></i></button></div></div><div><script src="/js/utils.js"></script><script src="/js/main.js"></script><script src="https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox.umd.min.js"></script><script>var preloader = {
  endLoading: () => {
    document.body.style.overflow = 'auto';
    document.getElementById('loading-box').classList.add("loaded")
  },
  initLoading: () => {
    document.body.style.overflow = '';
    document.getElementById('loading-box').classList.remove("loaded")

  }
}
window.addEventListener('load',preloader.endLoading())</script><div class="js-pjax"><script>function loadValine () {
  function initValine () {
    const valine = new Valine(Object.assign({
      el: '#vcomment',
      appId: '',
      appKey: '',
      avatar: 'monsterid',
      serverURLs: '',
      emojiMaps: "",
      path: window.location.pathname,
      visitor: false
    }, null))
  }

  if (typeof Valine === 'function') initValine() 
  else getScript('https://cdn.jsdelivr.net/npm/valine/dist/Valine.min.js').then(initValine)
}

if ('Valine' === 'Valine' || !true) {
  if (true) btf.loadComment(document.getElementById('vcomment'),loadValine)
  else setTimeout(loadValine, 0)
} else {
  function loadOtherComment () {
    loadValine()
  }
}</script><script>function loadDisqus () {
  var disqus_config = function () {
    this.page.url = 'http://example.com/2022/04/21/netty/Netty%E5%85%A5%E9%97%A8-%E7%AC%AC%E4%B8%89%E8%AF%9D/'
    this.page.identifier = '/2022/04/21/netty/Netty%E5%85%A5%E9%97%A8-%E7%AC%AC%E4%B8%89%E8%AF%9D/'
    this.page.title = 'Netty入门-第三话'
  };

  window.disqusReset = () => {
    DISQUS.reset({
      reload: true,
      config: disqus_config
    })
  }

  if (window.DISQUS) disqusReset()
  else {
    (function() { 
      var d = document, s = d.createElement('script');
      s.src = 'https://.disqus.com/embed.js';
      s.setAttribute('data-timestamp', +new Date());
      (d.head || d.body).appendChild(s);
    })();
  }

  document.getElementById('darkmode').addEventListener('click', () => {
    setTimeout(() => window.disqusReset(), 200)
  })
}

if ('Valine' === 'Disqus' || !true) {
  if (true) btf.loadComment(document.getElementById('disqus_thread'), loadDisqus)
  else loadDisqus()
} else {
  function loadOtherComment () {
    loadDisqus()
  }
}
</script></div><script async src="https://npm.elemecdn.com/tzy-blog/lib/js/other/sakura.js"></script><script defer="defer" id="ribbon" src="https://cdn.jsdelivr.net/npm/butterfly-extsrc/dist/canvas-ribbon.min.js" size="150" alpha="0.6" zIndex="-1" mobile="false" data-click="false"></script><script defer="defer" id="fluttering_ribbon" mobile="false" src="https://cdn.jsdelivr.net/npm/butterfly-extsrc/dist/canvas-fluttering-ribbon.min.js"></script><script id="click-show-text" src="https://cdn.jsdelivr.net/npm/butterfly-extsrc/dist/click-show-text.min.js" data-mobile="false" data-text="你好,大帅哥,大美女" data-fontsize="15px" data-random="true" async="async"></script><script src="https://cdn.jsdelivr.net/npm/pjax/pjax.min.js"></script><script>let pjaxSelectors = ["head > title","#config-diff","#body-wrap","#rightside-config-hide","#rightside-config-show",".js-pjax"]

var pjax = new Pjax({
  elements: 'a:not([target="_blank"])',
  selectors: pjaxSelectors,
  cacheBust: false,
  analytics: false,
  scrollRestoration: false
})

document.addEventListener('pjax:send', function () {

  // removeEventListener scroll 
  window.tocScrollFn && window.removeEventListener('scroll', window.tocScrollFn)
  window.scrollCollect && window.removeEventListener('scroll', scrollCollect)

  typeof preloader === 'object' && preloader.initLoading()
  document.getElementById('rightside').style.cssText = "opacity: ''; transform: ''"
  
  if (window.aplayers) {
    for (let i = 0; i < window.aplayers.length; i++) {
      if (!window.aplayers[i].options.fixed) {
        window.aplayers[i].destroy()
      }
    }
  }

  typeof typed === 'object' && typed.destroy()

  //reset readmode
  const $bodyClassList = document.body.classList
  $bodyClassList.contains('read-mode') && $bodyClassList.remove('read-mode')

  typeof disqusjs === 'object' && disqusjs.destroy()
})

document.addEventListener('pjax:complete', function () {
  window.refreshFn()

  document.querySelectorAll('script[data-pjax]').forEach(item => {
    const newScript = document.createElement('script')
    const content = item.text || item.textContent || item.innerHTML || ""
    Array.from(item.attributes).forEach(attr => newScript.setAttribute(attr.name, attr.value))
    newScript.appendChild(document.createTextNode(content))
    item.parentNode.replaceChild(newScript, item)
  })

  GLOBAL_CONFIG.islazyload && window.lazyLoadInstance.update()

  typeof chatBtnFn === 'function' && chatBtnFn()
  typeof panguInit === 'function' && panguInit()

  // google analytics
  typeof gtag === 'function' && gtag('config', '', {'page_path': window.location.pathname});

  // baidu analytics
  typeof _hmt === 'object' && _hmt.push(['_trackPageview',window.location.pathname]);

  typeof loadMeting === 'function' && document.getElementsByClassName('aplayer').length && loadMeting()

  // prismjs
  typeof Prism === 'object' && Prism.highlightAll()

  typeof preloader === 'object' && preloader.endLoading()
})

document.addEventListener('pjax:error', (e) => {
  if (e.request.status === 404) {
    pjax.loadUrl('/404.html')
  }
})</script><script async data-pjax src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script></div><script src="/live2dw/lib/L2Dwidget.min.js?094cbace49a39548bed64abff5988b05"></script><script>L2Dwidget.init({"tagMode":false,"debug":false,"model":{"jsonPath":"/live2dw/assets/shizuku.model.json"},"display":{"position":"right","width":150,"height":300,"hOffset":20,"vOffset":-20},"mobile":{"show":false},"log":false,"pluginJsPath":"lib/","pluginModelPath":"assets/","pluginRootPath":"live2dw/"});</script></body></html>